]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/game/AFEntity.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / game / AFEntity.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Game_local.h"
33
34
35 /*
36 ===============================================================================
37
38   idMultiModelAF
39
40 ===============================================================================
41 */
42
43 CLASS_DECLARATION( idEntity, idMultiModelAF )
44 END_CLASS
45
46 /*
47 ================
48 idMultiModelAF::Spawn
49 ================
50 */
51 void idMultiModelAF::Spawn( void ) {
52         physicsObj.SetSelf( this );
53 }
54
55 /*
56 ================
57 idMultiModelAF::~idMultiModelAF
58 ================
59 */
60 idMultiModelAF::~idMultiModelAF( void ) {
61         int i;
62
63         for ( i = 0; i < modelDefHandles.Num(); i++ ) {
64                 if ( modelDefHandles[i] != -1 ) {
65                         gameRenderWorld->FreeEntityDef( modelDefHandles[i] );
66                         modelDefHandles[i] = -1;
67                 }
68         }
69 }
70
71 /*
72 ================
73 idMultiModelAF::SetModelForId
74 ================
75 */
76 void idMultiModelAF::SetModelForId( int id, const idStr &modelName ) {
77         modelHandles.AssureSize( id+1, NULL );
78         modelDefHandles.AssureSize( id+1, -1 );
79         modelHandles[id] = renderModelManager->FindModel( modelName );
80 }
81
82 /*
83 ================
84 idMultiModelAF::Present
85 ================
86 */
87 void idMultiModelAF::Present( void ) {
88         int i;
89
90         // don't present to the renderer if the entity hasn't changed
91         if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
92                 return;
93         }
94         BecomeInactive( TH_UPDATEVISUALS );
95
96         for ( i = 0; i < modelHandles.Num(); i++ ) {
97
98                 if ( !modelHandles[i] ) {
99                         continue;
100                 }
101
102                 renderEntity.origin = physicsObj.GetOrigin( i );
103                 renderEntity.axis = physicsObj.GetAxis( i );
104                 renderEntity.hModel = modelHandles[i];
105                 renderEntity.bodyId = i;
106
107                 // add to refresh list
108                 if ( modelDefHandles[i] == -1 ) {
109                         modelDefHandles[i] = gameRenderWorld->AddEntityDef( &renderEntity );
110                 } else {
111                         gameRenderWorld->UpdateEntityDef( modelDefHandles[i], &renderEntity );
112                 }
113         }
114 }
115
116 /*
117 ================
118 idMultiModelAF::Think
119 ================
120 */
121 void idMultiModelAF::Think( void ) {
122         RunPhysics();
123         Present();
124 }
125
126
127 /*
128 ===============================================================================
129
130   idChain
131
132 ===============================================================================
133 */
134
135 CLASS_DECLARATION( idMultiModelAF, idChain )
136 END_CLASS
137
138 /*
139 ================
140 idChain::BuildChain
141
142   builds a chain hanging down from the ceiling
143   the highest link is a child of the link below it etc.
144   this allows an object to be attached to multiple chains while keeping a single tree structure
145 ================
146 */
147 void idChain::BuildChain( const idStr &name, const idVec3 &origin, float linkLength, float linkWidth, float density, int numLinks, bool bindToWorld ) {
148         int i;
149         float halfLinkLength = linkLength * 0.5f;
150         idTraceModel trm;
151         idClipModel *clip;
152         idAFBody *body, *lastBody;
153         idAFConstraint_BallAndSocketJoint *bsj;
154         idAFConstraint_UniversalJoint *uj;
155         idVec3 org;
156
157         // create a trace model
158         trm = idTraceModel( linkLength, linkWidth );
159         trm.Translate( -trm.offset );
160
161         org = origin - idVec3( 0, 0, halfLinkLength );
162
163         lastBody = NULL;
164         for ( i = 0; i < numLinks; i++ ) {
165
166                 // add body
167                 clip = new idClipModel( trm );
168                 clip->SetContents( CONTENTS_SOLID );
169                 clip->Link( gameLocal.clip, this, 0, org, mat3_identity );
170                 body = new idAFBody( name + idStr(i), clip, density );
171                 physicsObj.AddBody( body );
172
173                 // visual model for body
174                 SetModelForId( physicsObj.GetBodyId( body ), spawnArgs.GetString( "model" ) );
175
176                 // add constraint
177                 if ( bindToWorld ) {
178                         if ( !lastBody ) {
179                                 uj = new idAFConstraint_UniversalJoint( name + idStr(i), body, lastBody );
180                                 uj->SetShafts( idVec3( 0, 0, -1 ), idVec3( 0, 0, 1 ) );
181                                 //uj->SetConeLimit( idVec3( 0, 0, -1 ), 30.0f );
182                                 //uj->SetPyramidLimit( idVec3( 0, 0, -1 ), idVec3( 1, 0, 0 ), 90.0f, 30.0f );
183                         }
184                         else {
185                                 uj = new idAFConstraint_UniversalJoint( name + idStr(i), lastBody, body );
186                                 uj->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) );
187                                 //uj->SetConeLimit( idVec3( 0, 0, 1 ), 30.0f );
188                         }
189                         uj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
190                         uj->SetFriction( 0.9f );
191                         physicsObj.AddConstraint( uj );
192                 }
193                 else {
194                         if ( lastBody ) {
195                                 bsj = new idAFConstraint_BallAndSocketJoint( "joint" + idStr(i), lastBody, body );
196                                 bsj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
197                                 bsj->SetConeLimit( idVec3( 0, 0, 1 ), 60.0f, idVec3( 0, 0, 1 ) );
198                                 physicsObj.AddConstraint( bsj );
199                         }
200                 }
201
202                 org[2] -= linkLength;
203
204                 lastBody = body;
205         }
206 }
207
208 /*
209 ================
210 idChain::Spawn
211 ================
212 */
213 void idChain::Spawn( void ) {
214         int numLinks;
215         float length, linkLength, linkWidth, density;
216         bool drop;
217         idVec3 origin;
218
219         spawnArgs.GetBool( "drop", "0", drop );
220         spawnArgs.GetInt( "links", "3", numLinks );
221         spawnArgs.GetFloat( "length", idStr( numLinks * 32.0f ), length );
222         spawnArgs.GetFloat( "width", "8", linkWidth );
223         spawnArgs.GetFloat( "density", "0.2", density );
224         linkLength = length / numLinks;
225         origin = GetPhysics()->GetOrigin();
226
227         // initialize physics
228         physicsObj.SetSelf( this );
229         physicsObj.SetGravity( gameLocal.GetGravity() );
230         physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY );
231         SetPhysics( &physicsObj );
232
233         BuildChain( "link", origin, linkLength, linkWidth, density, numLinks, !drop );
234 }
235
236 /*
237 ===============================================================================
238
239   idAFAttachment
240
241 ===============================================================================
242 */
243
244 CLASS_DECLARATION( idAnimatedEntity, idAFAttachment )
245 END_CLASS
246
247 /*
248 =====================
249 idAFAttachment::idAFAttachment
250 =====================
251 */
252 idAFAttachment::idAFAttachment( void ) {
253         body                    = NULL;
254         combatModel             = NULL;
255         idleAnim                = 0;
256         attachJoint             = INVALID_JOINT;
257 }
258
259 /*
260 =====================
261 idAFAttachment::~idAFAttachment
262 =====================
263 */
264 idAFAttachment::~idAFAttachment( void ) {
265
266         StopSound( SND_CHANNEL_ANY, false );
267
268         delete combatModel;
269         combatModel = NULL;
270 }
271
272 /*
273 =====================
274 idAFAttachment::Spawn
275 =====================
276 */
277 void idAFAttachment::Spawn( void ) {
278         idleAnim = animator.GetAnim( "idle" );
279 }
280
281 /*
282 =====================
283 idAFAttachment::SetBody
284 =====================
285 */
286 void idAFAttachment::SetBody( idEntity *bodyEnt, const char *model, jointHandle_t attachJoint ) {
287         bool bleed;
288
289         body = bodyEnt;
290         this->attachJoint = attachJoint;
291         SetModel( model );
292         fl.takedamage = true;
293
294         bleed = body->spawnArgs.GetBool( "bleed" );
295         spawnArgs.SetBool( "bleed", bleed );
296 }
297
298 /*
299 =====================
300 idAFAttachment::ClearBody
301 =====================
302 */
303 void idAFAttachment::ClearBody( void ) {
304         body = NULL;
305         attachJoint = INVALID_JOINT;
306         Hide();
307 }
308
309 /*
310 =====================
311 idAFAttachment::GetBody
312 =====================
313 */
314 idEntity *idAFAttachment::GetBody( void ) const {
315         return body;
316 }
317
318 /*
319 ================
320 idAFAttachment::Save
321
322 archive object for savegame file
323 ================
324 */
325 void idAFAttachment::Save( idSaveGame *savefile ) const {
326         savefile->WriteObject( body );
327         savefile->WriteInt( idleAnim );
328         savefile->WriteJoint( attachJoint );
329 }
330
331 /*
332 ================
333 idAFAttachment::Restore
334
335 unarchives object from save game file
336 ================
337 */
338 void idAFAttachment::Restore( idRestoreGame *savefile ) {
339         savefile->ReadObject( reinterpret_cast<idClass *&>( body ) );
340         savefile->ReadInt( idleAnim );
341         savefile->ReadJoint( attachJoint );
342
343         SetCombatModel();
344         LinkCombat();
345 }
346
347 /*
348 ================
349 idAFAttachment::Hide
350 ================
351 */
352 void idAFAttachment::Hide( void ) {
353         idEntity::Hide();
354         UnlinkCombat();
355 }
356
357 /*
358 ================
359 idAFAttachment::Show
360 ================
361 */
362 void idAFAttachment::Show( void ) {
363         idEntity::Show();
364         LinkCombat();
365 }
366
367 /*
368 ============
369 idAFAttachment::Damage
370
371 Pass damage to body at the bindjoint
372 ============
373 */
374 void idAFAttachment::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, 
375         const char *damageDefName, const float damageScale, const int location ) {
376         
377         if ( body ) {
378                 body->Damage( inflictor, attacker, dir, damageDefName, damageScale, attachJoint );
379         }
380 }
381
382 /*
383 ================
384 idAFAttachment::AddDamageEffect
385 ================
386 */
387 void idAFAttachment::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
388         if ( body ) {
389                 trace_t c = collision;
390                 c.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint );
391                 body->AddDamageEffect( c, velocity, damageDefName );
392         }
393 }
394
395 /*
396 ================
397 idAFAttachment::GetImpactInfo
398 ================
399 */
400 void idAFAttachment::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
401         if ( body ) {
402                 body->GetImpactInfo( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, info );
403         } else {
404                 idEntity::GetImpactInfo( ent, id, point, info );
405         }
406 }
407
408 /*
409 ================
410 idAFAttachment::ApplyImpulse
411 ================
412 */
413 void idAFAttachment::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
414         if ( body ) {
415                 body->ApplyImpulse( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, impulse );
416         } else {
417                 idEntity::ApplyImpulse( ent, id, point, impulse );
418         }
419 }
420
421 /*
422 ================
423 idAFAttachment::AddForce
424 ================
425 */
426 void idAFAttachment::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
427         if ( body ) {
428                 body->AddForce( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, force );
429         } else {
430                 idEntity::AddForce( ent, id, point, force );
431         }
432 }
433
434 /*
435 ================
436 idAFAttachment::PlayIdleAnim
437 ================
438 */
439 void idAFAttachment::PlayIdleAnim( int blendTime ) {
440         if ( idleAnim && ( idleAnim != animator.CurrentAnim( ANIMCHANNEL_ALL )->AnimNum() ) ) {
441                 animator.CycleAnim( ANIMCHANNEL_ALL, idleAnim, gameLocal.time, blendTime );
442         }
443 }
444
445 /*
446 ================
447 idAfAttachment::Think
448 ================
449 */
450 void idAFAttachment::Think( void ) {
451         idAnimatedEntity::Think();
452         if ( thinkFlags & TH_UPDATEPARTICLES ) {
453                 UpdateDamageEffects();
454         }
455 }
456
457 /*
458 ================
459 idAFAttachment::SetCombatModel
460 ================
461 */
462 void idAFAttachment::SetCombatModel( void ) {
463         if ( combatModel ) {
464                 combatModel->Unlink();
465                 combatModel->LoadModel( modelDefHandle );
466         } else {
467                 combatModel = new idClipModel( modelDefHandle );
468         }
469         combatModel->SetOwner( body );
470 }
471
472 /*
473 ================
474 idAFAttachment::GetCombatModel
475 ================
476 */
477 idClipModel *idAFAttachment::GetCombatModel( void ) const {
478         return combatModel;
479 }
480
481 /*
482 ================
483 idAFAttachment::LinkCombat
484 ================
485 */
486 void idAFAttachment::LinkCombat( void ) {
487         if ( fl.hidden ) {
488                 return;
489         }
490
491         if ( combatModel ) {
492                 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
493         }
494 }
495
496 /*
497 ================
498 idAFAttachment::UnlinkCombat
499 ================
500 */
501 void idAFAttachment::UnlinkCombat( void ) {
502         if ( combatModel ) {
503                 combatModel->Unlink();
504         }
505 }
506
507
508 /*
509 ===============================================================================
510
511   idAFEntity_Base
512
513 ===============================================================================
514 */
515
516 const idEventDef EV_SetConstraintPosition( "SetConstraintPosition", "sv" );
517
518 CLASS_DECLARATION( idAnimatedEntity, idAFEntity_Base )
519         EVENT( EV_SetConstraintPosition,        idAFEntity_Base::Event_SetConstraintPosition )
520 END_CLASS
521
522 static const float BOUNCE_SOUND_MIN_VELOCITY    = 80.0f;
523 static const float BOUNCE_SOUND_MAX_VELOCITY    = 200.0f;
524
525 /*
526 ================
527 idAFEntity_Base::idAFEntity_Base
528 ================
529 */
530 idAFEntity_Base::idAFEntity_Base( void ) {
531         combatModel = NULL;
532         combatModelContents = 0;
533         nextSoundTime = 0;
534         spawnOrigin.Zero();
535         spawnAxis.Identity();
536 }
537
538 /*
539 ================
540 idAFEntity_Base::~idAFEntity_Base
541 ================
542 */
543 idAFEntity_Base::~idAFEntity_Base( void ) {
544         delete combatModel;
545         combatModel = NULL;
546 }
547
548 /*
549 ================
550 idAFEntity_Base::Save
551 ================
552 */
553 void idAFEntity_Base::Save( idSaveGame *savefile ) const {
554         savefile->WriteInt( combatModelContents );
555         savefile->WriteClipModel( combatModel );
556         savefile->WriteVec3( spawnOrigin );
557         savefile->WriteMat3( spawnAxis );
558         savefile->WriteInt( nextSoundTime );
559         af.Save( savefile );
560 }
561
562 /*
563 ================
564 idAFEntity_Base::Restore
565 ================
566 */
567 void idAFEntity_Base::Restore( idRestoreGame *savefile ) {
568         savefile->ReadInt( combatModelContents );
569         savefile->ReadClipModel( combatModel );
570         savefile->ReadVec3( spawnOrigin );
571         savefile->ReadMat3( spawnAxis );
572         savefile->ReadInt( nextSoundTime );
573         LinkCombat();
574
575         af.Restore( savefile );
576 }
577
578 /*
579 ================
580 idAFEntity_Base::Spawn
581 ================
582 */
583 void idAFEntity_Base::Spawn( void ) {
584         spawnOrigin = GetPhysics()->GetOrigin();
585         spawnAxis = GetPhysics()->GetAxis();
586         nextSoundTime = 0;
587 }
588
589 /*
590 ================
591 idAFEntity_Base::LoadAF
592 ================
593 */
594 bool idAFEntity_Base::LoadAF( void ) {
595         idStr fileName;
596
597         if ( !spawnArgs.GetString( "articulatedFigure", "*unknown*", fileName ) ) {
598                 return false;
599         }
600
601         af.SetAnimator( GetAnimator() );
602         if ( !af.Load( this, fileName ) ) {
603                 gameLocal.Error( "idAFEntity_Base::LoadAF: Couldn't load af file '%s' on entity '%s'", fileName.c_str(), name.c_str() );
604         }
605
606         af.Start();
607
608         af.GetPhysics()->Rotate( spawnAxis.ToRotation() );
609         af.GetPhysics()->Translate( spawnOrigin );
610
611         LoadState( spawnArgs );
612
613         af.UpdateAnimation();
614         animator.CreateFrame( gameLocal.time, true );
615         UpdateVisuals();
616
617         return true;
618 }
619
620 /*
621 ================
622 idAFEntity_Base::Think
623 ================
624 */
625 void idAFEntity_Base::Think( void ) {
626         RunPhysics();
627         UpdateAnimation();
628         if ( thinkFlags & TH_UPDATEVISUALS ) {
629                 Present();
630                 LinkCombat();
631         }
632 }
633
634 /*
635 ================
636 idAFEntity_Base::BodyForClipModelId
637 ================
638 */
639 int idAFEntity_Base::BodyForClipModelId( int id ) const {
640         return af.BodyForClipModelId( id );
641 }
642
643 /*
644 ================
645 idAFEntity_Base::SaveState
646 ================
647 */
648 void idAFEntity_Base::SaveState( idDict &args ) const {
649         const idKeyValue *kv;
650
651         // save the ragdoll pose
652         af.SaveState( args );
653
654         // save all the bind constraints
655         kv = spawnArgs.MatchPrefix( "bindConstraint ", NULL );
656         while ( kv ) {
657                 args.Set( kv->GetKey(), kv->GetValue() );
658                 kv = spawnArgs.MatchPrefix( "bindConstraint ", kv );
659         }
660
661         // save the bind if it exists
662         kv = spawnArgs.FindKey( "bind" );
663         if ( kv ) {
664                 args.Set( kv->GetKey(), kv->GetValue() );
665         }
666         kv = spawnArgs.FindKey( "bindToJoint" );
667         if ( kv ) {
668                 args.Set( kv->GetKey(), kv->GetValue() );
669         }
670         kv = spawnArgs.FindKey( "bindToBody" );
671         if ( kv ) {
672                 args.Set( kv->GetKey(), kv->GetValue() );
673         }
674 }
675
676 /*
677 ================
678 idAFEntity_Base::LoadState
679 ================
680 */
681 void idAFEntity_Base::LoadState( const idDict &args ) {
682         af.LoadState( args );
683 }
684
685 /*
686 ================
687 idAFEntity_Base::AddBindConstraints
688 ================
689 */
690 void idAFEntity_Base::AddBindConstraints( void ) {
691         af.AddBindConstraints();
692 }
693
694 /*
695 ================
696 idAFEntity_Base::RemoveBindConstraints
697 ================
698 */
699 void idAFEntity_Base::RemoveBindConstraints( void ) {
700         af.RemoveBindConstraints();
701 }
702
703 /*
704 ================
705 idAFEntity_Base::GetImpactInfo
706 ================
707 */
708 void idAFEntity_Base::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
709         if ( af.IsActive() ) {
710                 af.GetImpactInfo( ent, id, point, info );
711         } else {
712                 idEntity::GetImpactInfo( ent, id, point, info );
713         }
714 }
715
716 /*
717 ================
718 idAFEntity_Base::ApplyImpulse
719 ================
720 */
721 void idAFEntity_Base::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
722         if ( af.IsLoaded() ) {
723                 af.ApplyImpulse( ent, id, point, impulse );
724         }
725         if ( !af.IsActive() ) {
726                 idEntity::ApplyImpulse( ent, id, point, impulse );
727         }
728 }
729
730 /*
731 ================
732 idAFEntity_Base::AddForce
733 ================
734 */
735 void idAFEntity_Base::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
736         if ( af.IsLoaded() ) {
737                 af.AddForce( ent, id, point, force );
738         }
739         if ( !af.IsActive() ) {
740                 idEntity::AddForce( ent, id, point, force );
741         }
742 }
743
744 /*
745 ================
746 idAFEntity_Base::Collide
747 ================
748 */
749 bool idAFEntity_Base::Collide( const trace_t &collision, const idVec3 &velocity ) {
750         float v, f;
751
752         if ( af.IsActive() ) {
753                 v = -( velocity * collision.c.normal );
754                 if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
755                         f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) );
756                         if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) {
757                                 // don't set the volume unless there is a bounce sound as it overrides the entire channel
758                                 // which causes footsteps on ai's to not honor their shader parms
759                                 SetSoundVolume( f );
760                         }
761                         nextSoundTime = gameLocal.time + 500;
762                 }
763         }
764
765         return false;
766 }
767
768 /*
769 ================
770 idAFEntity_Base::GetPhysicsToVisualTransform
771 ================
772 */
773 bool idAFEntity_Base::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
774         if ( af.IsActive() ) {
775                 af.GetPhysicsToVisualTransform( origin, axis );
776                 return true;
777         }
778         return idEntity::GetPhysicsToVisualTransform( origin, axis );
779 }
780
781 /*
782 ================
783 idAFEntity_Base::UpdateAnimationControllers
784 ================
785 */
786 bool idAFEntity_Base::UpdateAnimationControllers( void ) {
787         if ( af.IsActive() ) {
788                 if ( af.UpdateAnimation() ) {
789                         return true;
790                 }
791         }
792         return false;
793 }
794
795 /*
796 ================
797 idAFEntity_Base::SetCombatModel
798 ================
799 */
800 void idAFEntity_Base::SetCombatModel( void ) {
801         if ( combatModel ) {
802                 combatModel->Unlink();
803                 combatModel->LoadModel( modelDefHandle );
804         } else {
805                 combatModel = new idClipModel( modelDefHandle );
806         }
807 }
808
809 /*
810 ================
811 idAFEntity_Base::GetCombatModel
812 ================
813 */
814 idClipModel *idAFEntity_Base::GetCombatModel( void ) const {
815         return combatModel;
816 }
817
818 /*
819 ================
820 idAFEntity_Base::SetCombatContents
821 ================
822 */
823 void idAFEntity_Base::SetCombatContents( bool enable ) {
824         assert( combatModel );
825         if ( enable && combatModelContents ) {
826                 assert( !combatModel->GetContents() );
827                 combatModel->SetContents( combatModelContents );
828                 combatModelContents = 0;
829         } else if ( !enable && combatModel->GetContents() ) {
830                 assert( !combatModelContents );
831                 combatModelContents = combatModel->GetContents();
832                 combatModel->SetContents( 0 );
833         }
834 }
835
836 /*
837 ================
838 idAFEntity_Base::LinkCombat
839 ================
840 */
841 void idAFEntity_Base::LinkCombat( void ) {
842         if ( fl.hidden ) {
843                 return;
844         }
845         if ( combatModel ) {
846                 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
847         }
848 }
849
850 /*
851 ================
852 idAFEntity_Base::UnlinkCombat
853 ================
854 */
855 void idAFEntity_Base::UnlinkCombat( void ) {
856         if ( combatModel ) {
857                 combatModel->Unlink();
858         }
859 }
860
861 /*
862 ================
863 idAFEntity_Base::FreeModelDef
864 ================
865 */
866 void idAFEntity_Base::FreeModelDef( void ) {
867         UnlinkCombat();
868         idEntity::FreeModelDef();
869 }
870
871 /*
872 ===============
873 idAFEntity_Base::ShowEditingDialog
874 ===============
875 */
876 void idAFEntity_Base::ShowEditingDialog( void ) {
877         common->InitTool( EDITOR_AF, &spawnArgs );
878 }
879
880 /*
881 ================
882 idAFEntity_Base::DropAFs
883
884   The entity should have the following key/value pairs set:
885         "def_drop<type>AF"              "af def"
886         "drop<type>Skin"                "skin name"
887   To drop multiple articulated figures the following key/value pairs can be used:
888         "def_drop<type>AF*"             "af def"
889   where * is an aribtrary string.
890 ================
891 */
892 void idAFEntity_Base::DropAFs( idEntity *ent, const char *type, idList<idEntity *> *list ) {
893         const idKeyValue *kv;
894         const char *skinName;
895         idEntity *newEnt;
896         idAFEntity_Base *af;
897         idDict args;
898         const idDeclSkin *skin;
899
900         // drop the articulated figures
901         kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), NULL );
902         while ( kv ) {
903
904                 args.Set( "classname", kv->GetValue() );
905                 gameLocal.SpawnEntityDef( args, &newEnt );
906
907                 if ( newEnt && newEnt->IsType( idAFEntity_Base::Type ) ) {
908                         af = static_cast<idAFEntity_Base *>(newEnt);
909                         af->GetPhysics()->SetOrigin( ent->GetPhysics()->GetOrigin() );
910                         af->GetPhysics()->SetAxis( ent->GetPhysics()->GetAxis() );
911                         af->af.SetupPose( ent, gameLocal.time );
912                         if ( list ) {
913                                 list->Append( af );
914                         }
915                 }
916
917                 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), kv );
918         }
919
920         // change the skin to hide all the dropped articulated figures
921         skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) );
922         if ( skinName[0] ) {
923                 skin = declManager->FindSkin( skinName );
924                 ent->SetSkin( skin );
925         }
926 }
927
928 /*
929 ================
930 idAFEntity_Base::Event_SetConstraintPosition
931 ================
932 */
933 void idAFEntity_Base::Event_SetConstraintPosition( const char *name, const idVec3 &pos ) {
934         af.SetConstraintPosition( name, pos );
935 }
936
937 /*
938 ===============================================================================
939
940 idAFEntity_Gibbable
941
942 ===============================================================================
943 */
944
945 const idEventDef EV_Gib( "gib", "s" );
946 const idEventDef EV_Gibbed( "<gibbed>" );
947
948 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Gibbable )
949         EVENT( EV_Gib,          idAFEntity_Gibbable::Event_Gib )
950         EVENT( EV_Gibbed,       idAFEntity_Base::Event_Remove )
951 END_CLASS
952
953
954 /*
955 ================
956 idAFEntity_Gibbable::idAFEntity_Gibbable
957 ================
958 */
959 idAFEntity_Gibbable::idAFEntity_Gibbable( void ) {
960         skeletonModel = NULL;
961         skeletonModelDefHandle = -1;
962         gibbed = false;
963 }
964
965 /*
966 ================
967 idAFEntity_Gibbable::~idAFEntity_Gibbable
968 ================
969 */
970 idAFEntity_Gibbable::~idAFEntity_Gibbable() {
971         if ( skeletonModelDefHandle != -1 ) {
972                 gameRenderWorld->FreeEntityDef( skeletonModelDefHandle );
973                 skeletonModelDefHandle = -1;
974         }
975 }
976
977 /*
978 ================
979 idAFEntity_Gibbable::Save
980 ================
981 */
982 void idAFEntity_Gibbable::Save( idSaveGame *savefile ) const {
983         savefile->WriteBool( gibbed );
984         savefile->WriteBool( combatModel != NULL );
985 }
986
987 /*
988 ================
989 idAFEntity_Gibbable::Restore
990 ================
991 */
992 void idAFEntity_Gibbable::Restore( idRestoreGame *savefile ) {
993         bool hasCombatModel;
994
995         savefile->ReadBool( gibbed );
996         savefile->ReadBool( hasCombatModel );
997
998         InitSkeletonModel();
999
1000         if ( hasCombatModel ) {
1001                 SetCombatModel();
1002                 LinkCombat();
1003         }
1004 }
1005
1006 /*
1007 ================
1008 idAFEntity_Gibbable::Spawn
1009 ================
1010 */
1011 void idAFEntity_Gibbable::Spawn( void ) {
1012         InitSkeletonModel();
1013
1014         gibbed = false;
1015 }
1016
1017 /*
1018 ================
1019 idAFEntity_Gibbable::InitSkeletonModel
1020 ================
1021 */
1022 void idAFEntity_Gibbable::InitSkeletonModel( void ) {
1023         const char *modelName;
1024         const idDeclModelDef *modelDef;
1025
1026         skeletonModel = NULL;
1027         skeletonModelDefHandle = -1;
1028
1029         modelName = spawnArgs.GetString( "model_gib" );
1030
1031         modelDef = NULL;
1032         if ( modelName[0] != '\0' ) {
1033                 modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
1034                 if ( modelDef ) {
1035                         skeletonModel = modelDef->ModelHandle();
1036                 } else {
1037                         skeletonModel = renderModelManager->FindModel( modelName );
1038                 }
1039                 if ( skeletonModel != NULL && renderEntity.hModel != NULL ) {
1040                         if ( skeletonModel->NumJoints() != renderEntity.hModel->NumJoints() ) {
1041                                 gameLocal.Error( "gib model '%s' has different number of joints than model '%s'",
1042                                                                         skeletonModel->Name(), renderEntity.hModel->Name() );
1043                         }
1044                 }
1045         }
1046 }
1047
1048 /*
1049 ================
1050 idAFEntity_Gibbable::Present
1051 ================
1052 */
1053 void idAFEntity_Gibbable::Present( void ) {
1054         renderEntity_t skeleton;
1055
1056         if ( !gameLocal.isNewFrame ) {
1057                 return;
1058         }
1059
1060         // don't present to the renderer if the entity hasn't changed
1061         if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
1062                 return;
1063         }
1064
1065         // update skeleton model
1066         if ( gibbed && !IsHidden() && skeletonModel != NULL ) {
1067                 skeleton = renderEntity;
1068                 skeleton.hModel = skeletonModel;
1069                 // add to refresh list
1070                 if ( skeletonModelDefHandle == -1 ) {
1071                         skeletonModelDefHandle = gameRenderWorld->AddEntityDef( &skeleton );
1072                 } else {
1073                         gameRenderWorld->UpdateEntityDef( skeletonModelDefHandle, &skeleton );
1074                 }
1075         }
1076
1077         idEntity::Present();
1078 }
1079
1080 /*
1081 ================
1082 idAFEntity_Gibbable::Damage
1083 ================
1084 */
1085 void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
1086         if ( !fl.takedamage ) {
1087                 return;
1088         }
1089         idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
1090         if ( health < -20 && spawnArgs.GetBool( "gib" ) ) {
1091                 Gib( dir, damageDefName );
1092         }
1093 }
1094
1095 /*
1096 =====================
1097 idAFEntity_Gibbable::SpawnGibs
1098 =====================
1099 */
1100 void idAFEntity_Gibbable::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
1101         int i;
1102         bool gibNonSolid;
1103         idVec3 entityCenter, velocity;
1104         idList<idEntity *> list;
1105
1106         assert( !gameLocal.isClient );
1107
1108         const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1109         if ( !damageDef ) {
1110                 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1111         }
1112
1113         // spawn gib articulated figures
1114         idAFEntity_Base::DropAFs( this, "gib", &list );
1115
1116         // spawn gib items
1117         idMoveableItem::DropItems( this, "gib", &list );
1118
1119         // blow out the gibs in the given direction away from the center of the entity
1120         entityCenter = GetPhysics()->GetAbsBounds().GetCenter();
1121         gibNonSolid = damageDef->GetBool( "gibNonSolid" );
1122         for ( i = 0; i < list.Num(); i++ ) {
1123                 if ( gibNonSolid ) {
1124                         list[i]->GetPhysics()->SetContents( 0 );
1125                         list[i]->GetPhysics()->SetClipMask( 0 );
1126                         list[i]->GetPhysics()->UnlinkClip();
1127                         list[i]->GetPhysics()->PutToRest();
1128                 } else {
1129                         list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE );
1130                         list[i]->GetPhysics()->SetClipMask( CONTENTS_SOLID );
1131                         velocity = list[i]->GetPhysics()->GetAbsBounds().GetCenter() - entityCenter;
1132                         velocity.NormalizeFast();
1133                         velocity += ( i & 1 ) ? dir : -dir;
1134                         list[i]->GetPhysics()->SetLinearVelocity( velocity * 75.0f );
1135                 }
1136                 list[i]->GetRenderEntity()->noShadow = true;
1137                 list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1138                 list[i]->PostEventSec( &EV_Remove, 4.0f );
1139         }
1140 }
1141
1142 /*
1143 ============
1144 idAFEntity_Gibbable::Gib
1145 ============
1146 */
1147 void idAFEntity_Gibbable::Gib( const idVec3 &dir, const char *damageDefName ) {
1148         // only gib once
1149         if ( gibbed ) {
1150                 return;
1151         }
1152
1153         const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1154         if ( !damageDef ) {
1155                 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1156         }
1157
1158         if ( damageDef->GetBool( "gibNonSolid" ) ) {
1159                 GetAFPhysics()->SetContents( 0 );
1160                 GetAFPhysics()->SetClipMask( 0 );
1161                 GetAFPhysics()->UnlinkClip();
1162                 GetAFPhysics()->PutToRest();
1163         } else {
1164                 GetAFPhysics()->SetContents( CONTENTS_CORPSE );
1165                 GetAFPhysics()->SetClipMask( CONTENTS_SOLID );
1166         }
1167
1168         UnlinkCombat();
1169
1170         if ( g_bloodEffects.GetBool() ) {
1171                 if ( gameLocal.time > gameLocal.GetGibTime() ) {
1172                         gameLocal.SetGibTime( gameLocal.time + GIB_DELAY );
1173                         SpawnGibs( dir, damageDefName );
1174                         renderEntity.noShadow = true;
1175                         renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1176                         StartSound( "snd_gibbed", SND_CHANNEL_ANY, 0, false, NULL );
1177                         gibbed = true;
1178                 }
1179         } else {
1180                 gibbed = true;
1181         }
1182
1183
1184         PostEventSec( &EV_Gibbed, 4.0f );
1185 }
1186
1187 /*
1188 ============
1189 idAFEntity_Gibbable::Event_Gib
1190 ============
1191 */
1192 void idAFEntity_Gibbable::Event_Gib( const char *damageDefName ) {
1193         Gib( idVec3( 0, 0, 1 ), damageDefName );
1194 }
1195
1196 /*
1197 ===============================================================================
1198
1199   idAFEntity_Generic
1200
1201 ===============================================================================
1202 */
1203
1204 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_Generic )
1205         EVENT( EV_Activate,                     idAFEntity_Generic::Event_Activate )
1206 END_CLASS
1207
1208 /*
1209 ================
1210 idAFEntity_Generic::idAFEntity_Generic
1211 ================
1212 */
1213 idAFEntity_Generic::idAFEntity_Generic( void ) {
1214         keepRunningPhysics = false;
1215 }
1216
1217 /*
1218 ================
1219 idAFEntity_Generic::~idAFEntity_Generic
1220 ================
1221 */
1222 idAFEntity_Generic::~idAFEntity_Generic( void ) {
1223 }
1224
1225 /*
1226 ================
1227 idAFEntity_Generic::Save
1228 ================
1229 */
1230 void idAFEntity_Generic::Save( idSaveGame *savefile ) const {
1231         savefile->WriteBool( keepRunningPhysics );
1232 }
1233
1234 /*
1235 ================
1236 idAFEntity_Generic::Restore
1237 ================
1238 */
1239 void idAFEntity_Generic::Restore( idRestoreGame *savefile ) {
1240         savefile->ReadBool( keepRunningPhysics );
1241 }
1242
1243 /*
1244 ================
1245 idAFEntity_Generic::Think
1246 ================
1247 */
1248 void idAFEntity_Generic::Think( void ) {
1249         idAFEntity_Base::Think();
1250
1251         if ( keepRunningPhysics ) {
1252                 BecomeActive( TH_PHYSICS );
1253         }
1254 }
1255
1256 /*
1257 ================
1258 idAFEntity_Generic::Spawn
1259 ================
1260 */
1261 void idAFEntity_Generic::Spawn( void ) {
1262         if ( !LoadAF() ) {
1263                 gameLocal.Error( "Couldn't load af file on entity '%s'", name.c_str() );
1264         }
1265
1266         SetCombatModel();
1267
1268         SetPhysics( af.GetPhysics() );
1269
1270         af.GetPhysics()->PutToRest();
1271         if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1272                 af.GetPhysics()->Activate();
1273         }
1274
1275         fl.takedamage = true;
1276 }
1277
1278 /*
1279 ================
1280 idAFEntity_Generic::Event_Activate
1281 ================
1282 */
1283 void idAFEntity_Generic::Event_Activate( idEntity *activator ) {
1284         float delay;
1285         idVec3 init_velocity, init_avelocity;
1286
1287         Show();
1288
1289         af.GetPhysics()->EnableImpact();
1290         af.GetPhysics()->Activate();
1291
1292         spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1293         spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1294
1295         delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1296         if ( delay == 0.0f ) {
1297                 af.GetPhysics()->SetLinearVelocity( init_velocity );
1298         } else {
1299                 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1300         }
1301
1302         delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1303         if ( delay == 0.0f ) {
1304                 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1305         } else {
1306                 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1307         }
1308 }
1309
1310
1311 /*
1312 ===============================================================================
1313
1314   idAFEntity_WithAttachedHead
1315
1316 ===============================================================================
1317 */
1318
1319 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_WithAttachedHead )
1320         EVENT( EV_Gib,                          idAFEntity_WithAttachedHead::Event_Gib )
1321         EVENT( EV_Activate,                     idAFEntity_WithAttachedHead::Event_Activate )
1322 END_CLASS
1323
1324 /*
1325 ================
1326 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead
1327 ================
1328 */
1329 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead() {
1330         head = NULL;
1331 }
1332
1333 /*
1334 ================
1335 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead
1336 ================
1337 */
1338 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead() {
1339         if ( head.GetEntity() ) {
1340                 head.GetEntity()->ClearBody();
1341                 head.GetEntity()->PostEventMS( &EV_Remove, 0 );
1342         }
1343 }
1344
1345 /*
1346 ================
1347 idAFEntity_WithAttachedHead::Spawn
1348 ================
1349 */
1350 void idAFEntity_WithAttachedHead::Spawn( void ) {
1351         SetupHead();
1352
1353         LoadAF();
1354
1355         SetCombatModel();
1356
1357         SetPhysics( af.GetPhysics() );
1358
1359         af.GetPhysics()->PutToRest();
1360         if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1361                 af.GetPhysics()->Activate();
1362         }
1363
1364         fl.takedamage = true;
1365
1366         if ( head.GetEntity() ) {
1367                 int anim = head.GetEntity()->GetAnimator()->GetAnim( "dead" );
1368
1369                 if ( anim ) {
1370                         head.GetEntity()->GetAnimator()->SetFrame( ANIMCHANNEL_ALL, anim, 0, gameLocal.time, 0 );
1371                 }
1372         }
1373 }
1374
1375 /*
1376 ================
1377 idAFEntity_WithAttachedHead::Save
1378 ================
1379 */
1380 void idAFEntity_WithAttachedHead::Save( idSaveGame *savefile ) const {
1381         head.Save( savefile );
1382 }
1383
1384 /*
1385 ================
1386 idAFEntity_WithAttachedHead::Restore
1387 ================
1388 */
1389 void idAFEntity_WithAttachedHead::Restore( idRestoreGame *savefile ) {
1390         head.Restore( savefile );
1391 }
1392
1393 /*
1394 ================
1395 idAFEntity_WithAttachedHead::SetupHead
1396 ================
1397 */
1398 void idAFEntity_WithAttachedHead::SetupHead( void ) {
1399         idAFAttachment          *headEnt;
1400         idStr                           jointName;
1401         const char                      *headModel;
1402         jointHandle_t           joint;
1403         idVec3                          origin;
1404         idMat3                          axis;
1405
1406         headModel = spawnArgs.GetString( "def_head", "" );
1407         if ( headModel[ 0 ] ) {
1408                 jointName = spawnArgs.GetString( "head_joint" );
1409                 joint = animator.GetJointHandle( jointName );
1410                 if ( joint == INVALID_JOINT ) {
1411                         gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
1412                 }
1413
1414                 headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, NULL ) );
1415                 headEnt->SetName( va( "%s_head", name.c_str() ) );
1416                 headEnt->SetBody( this, headModel, joint );
1417                 headEnt->SetCombatModel();
1418                 head = headEnt;
1419
1420                 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
1421                 origin = renderEntity.origin + origin * renderEntity.axis;
1422                 headEnt->SetOrigin( origin );
1423                 headEnt->SetAxis( renderEntity.axis );
1424                 headEnt->BindToJoint( this, joint, true );
1425         }
1426 }
1427
1428 /*
1429 ================
1430 idAFEntity_WithAttachedHead::Think
1431 ================
1432 */
1433 void idAFEntity_WithAttachedHead::Think( void ) {
1434         idAFEntity_Base::Think();
1435 }
1436
1437 /*
1438 ================
1439 idAFEntity_WithAttachedHead::LinkCombat
1440 ================
1441 */
1442 void idAFEntity_WithAttachedHead::LinkCombat( void ) {
1443         idAFAttachment *headEnt;
1444
1445         if ( fl.hidden ) {
1446                 return;
1447         }
1448
1449         if ( combatModel ) {
1450                 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1451         }
1452         headEnt = head.GetEntity();
1453         if ( headEnt ) {
1454                 headEnt->LinkCombat();
1455         }
1456 }
1457
1458 /*
1459 ================
1460 idAFEntity_WithAttachedHead::UnlinkCombat
1461 ================
1462 */
1463 void idAFEntity_WithAttachedHead::UnlinkCombat( void ) {
1464         idAFAttachment *headEnt;
1465
1466         if ( combatModel ) {
1467                 combatModel->Unlink();
1468         }
1469         headEnt = head.GetEntity();
1470         if ( headEnt ) {
1471                 headEnt->UnlinkCombat();
1472         }
1473 }
1474
1475 /*
1476 ================
1477 idAFEntity_WithAttachedHead::Hide
1478 ================
1479 */
1480 void idAFEntity_WithAttachedHead::Hide( void ) {
1481         idAFEntity_Base::Hide();
1482         if ( head.GetEntity() ) {
1483                 head.GetEntity()->Hide();
1484         }
1485         UnlinkCombat();
1486 }
1487
1488 /*
1489 ================
1490 idAFEntity_WithAttachedHead::Show
1491 ================
1492 */
1493 void idAFEntity_WithAttachedHead::Show( void ) {
1494         idAFEntity_Base::Show();
1495         if ( head.GetEntity() ) {
1496                 head.GetEntity()->Show();
1497         }
1498         LinkCombat();
1499 }
1500
1501 /*
1502 ================
1503 idAFEntity_WithAttachedHead::ProjectOverlay
1504 ================
1505 */
1506 void idAFEntity_WithAttachedHead::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1507
1508         idEntity::ProjectOverlay( origin, dir, size, material );
1509
1510         if ( head.GetEntity() ) {
1511                 head.GetEntity()->ProjectOverlay( origin, dir, size, material );
1512         }
1513 }
1514
1515 /*
1516 ============
1517 idAFEntity_WithAttachedHead::Gib
1518 ============
1519 */
1520 void idAFEntity_WithAttachedHead::Gib( const idVec3 &dir, const char *damageDefName ) {
1521         // only gib once
1522         if ( gibbed ) {
1523                 return;
1524         }
1525         idAFEntity_Gibbable::Gib( dir, damageDefName );
1526         if ( head.GetEntity() ) {
1527                 head.GetEntity()->Hide();
1528         }
1529 }
1530
1531 /*
1532 ============
1533 idAFEntity_WithAttachedHead::Event_Gib
1534 ============
1535 */
1536 void idAFEntity_WithAttachedHead::Event_Gib( const char *damageDefName ) {
1537         Gib( idVec3( 0, 0, 1 ), damageDefName );
1538 }
1539
1540 /*
1541 ================
1542 idAFEntity_WithAttachedHead::Event_Activate
1543 ================
1544 */
1545 void idAFEntity_WithAttachedHead::Event_Activate( idEntity *activator ) {
1546         float delay;
1547         idVec3 init_velocity, init_avelocity;
1548
1549         Show();
1550
1551         af.GetPhysics()->EnableImpact();
1552         af.GetPhysics()->Activate();
1553
1554         spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1555         spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1556
1557         delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1558         if ( delay == 0.0f ) {
1559                 af.GetPhysics()->SetLinearVelocity( init_velocity );
1560         } else {
1561                 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1562         }
1563
1564         delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1565         if ( delay == 0.0f ) {
1566                 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1567         } else {
1568                 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1569         }
1570 }
1571
1572
1573 /*
1574 ===============================================================================
1575
1576   idAFEntity_Vehicle
1577
1578 ===============================================================================
1579 */
1580
1581 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Vehicle )
1582 END_CLASS
1583
1584 /*
1585 ================
1586 idAFEntity_Vehicle::idAFEntity_Vehicle
1587 ================
1588 */
1589 idAFEntity_Vehicle::idAFEntity_Vehicle( void ) {
1590         player                          = NULL;
1591         eyesJoint                       = INVALID_JOINT;
1592         steeringWheelJoint      = INVALID_JOINT;
1593         wheelRadius                     = 0.0f;
1594         steerAngle                      = 0.0f;
1595         steerSpeed                      = 0.0f;
1596         dustSmoke                       = NULL;
1597 }
1598
1599 /*
1600 ================
1601 idAFEntity_Vehicle::Spawn
1602 ================
1603 */
1604 void idAFEntity_Vehicle::Spawn( void ) {
1605         const char *eyesJointName = spawnArgs.GetString( "eyesJoint", "eyes" );
1606         const char *steeringWheelJointName = spawnArgs.GetString( "steeringWheelJoint", "steeringWheel" );
1607
1608         LoadAF();
1609
1610         SetCombatModel();
1611
1612         SetPhysics( af.GetPhysics() );
1613
1614         fl.takedamage = true;
1615
1616         if ( !eyesJointName[0] ) {
1617                 gameLocal.Error( "idAFEntity_Vehicle '%s' no eyes joint specified", name.c_str() );
1618         }
1619         eyesJoint = animator.GetJointHandle( eyesJointName );
1620         if ( !steeringWheelJointName[0] ) {
1621                 gameLocal.Error( "idAFEntity_Vehicle '%s' no steering wheel joint specified", name.c_str() );
1622         }
1623         steeringWheelJoint = animator.GetJointHandle( steeringWheelJointName );
1624
1625         spawnArgs.GetFloat( "wheelRadius", "20", wheelRadius );
1626         spawnArgs.GetFloat( "steerSpeed", "5", steerSpeed ); 
1627
1628         player = NULL;
1629         steerAngle = 0.0f;
1630
1631         const char *smokeName = spawnArgs.GetString( "smoke_vehicle_dust", "muzzlesmoke" );
1632         if ( *smokeName != '\0' ) {
1633                 dustSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1634         }
1635 }
1636
1637 /*
1638 ================
1639 idAFEntity_Vehicle::Use
1640 ================
1641 */
1642 void idAFEntity_Vehicle::Use( idPlayer *other ) {
1643         idVec3 origin;
1644         idMat3 axis;
1645
1646         if ( player ) {
1647                 if ( player == other ) {
1648                         other->Unbind();
1649                         player = NULL;
1650
1651                         af.GetPhysics()->SetComeToRest( true );
1652                 }
1653         }
1654         else {
1655                 player = other;
1656                 animator.GetJointTransform( eyesJoint, gameLocal.time, origin, axis );
1657                 origin = renderEntity.origin + origin * renderEntity.axis;
1658                 player->GetPhysics()->SetOrigin( origin );
1659                 player->BindToBody( this, 0, true );
1660
1661                 af.GetPhysics()->SetComeToRest( false );
1662                 af.GetPhysics()->Activate();
1663         }
1664 }
1665
1666 /*
1667 ================
1668 idAFEntity_Vehicle::GetSteerAngle
1669 ================
1670 */
1671 float idAFEntity_Vehicle::GetSteerAngle( void ) {
1672         float idealSteerAngle, angleDelta;
1673
1674         idealSteerAngle = player->usercmd.rightmove * ( 30.0f / 128.0f );
1675         angleDelta = idealSteerAngle - steerAngle;
1676
1677         if ( angleDelta > steerSpeed ) {
1678                 steerAngle += steerSpeed;
1679         } else if ( angleDelta < -steerSpeed ) {
1680                 steerAngle -= steerSpeed;
1681         } else {
1682                 steerAngle = idealSteerAngle;
1683         }
1684
1685         return steerAngle;
1686 }
1687
1688
1689 /*
1690 ===============================================================================
1691
1692   idAFEntity_VehicleSimple
1693
1694 ===============================================================================
1695 */
1696
1697 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSimple )
1698 END_CLASS
1699
1700 /*
1701 ================
1702 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple
1703 ================
1704 */
1705 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple( void ) {
1706         int i;
1707         for ( i = 0; i < 4; i++ ) {
1708                 suspension[i] = NULL;
1709         }
1710 }
1711
1712 /*
1713 ================
1714 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple
1715 ================
1716 */
1717 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple( void ) {
1718         delete wheelModel;
1719         wheelModel = NULL;
1720 }
1721
1722 /*
1723 ================
1724 idAFEntity_VehicleSimple::Spawn
1725 ================
1726 */
1727 void idAFEntity_VehicleSimple::Spawn( void ) {
1728         static const char *wheelJointKeys[] = {
1729                 "wheelJointFrontLeft",
1730                 "wheelJointFrontRight",
1731                 "wheelJointRearLeft",
1732                 "wheelJointRearRight"
1733         };
1734         static idVec3 wheelPoly[4] = { idVec3( 2, 2, 0 ), idVec3( 2, -2, 0 ), idVec3( -2, -2, 0 ), idVec3( -2, 2, 0 ) };
1735
1736         int i;
1737         idVec3 origin;
1738         idMat3 axis;
1739         idTraceModel trm;
1740
1741         trm.SetupPolygon( wheelPoly, 4 );
1742         trm.Translate( idVec3( 0, 0, -wheelRadius ) );
1743         wheelModel = new idClipModel( trm );
1744
1745         for ( i = 0; i < 4; i++ ) {
1746                 const char *wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
1747                 if ( !wheelJointName[0] ) {
1748                         gameLocal.Error( "idAFEntity_VehicleSimple '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
1749                 }
1750                 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
1751                 if ( wheelJoints[i] == INVALID_JOINT ) {
1752                         gameLocal.Error( "idAFEntity_VehicleSimple '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
1753                 }
1754
1755                 GetAnimator()->GetJointTransform( wheelJoints[i], 0, origin, axis );
1756                 origin = renderEntity.origin + origin * renderEntity.axis;
1757
1758                 suspension[i] = new idAFConstraint_Suspension();
1759                 suspension[i]->Setup( va( "suspension%d", i ), af.GetPhysics()->GetBody( 0 ), origin, af.GetPhysics()->GetAxis( 0 ), wheelModel );
1760                 suspension[i]->SetSuspension(   g_vehicleSuspensionUp.GetFloat(),
1761                                                                                 g_vehicleSuspensionDown.GetFloat(),
1762                                                                                 g_vehicleSuspensionKCompress.GetFloat(),
1763                                                                                 g_vehicleSuspensionDamping.GetFloat(),
1764                                                                                 g_vehicleTireFriction.GetFloat() );
1765
1766                 af.GetPhysics()->AddConstraint( suspension[i] );
1767         }
1768
1769         memset( wheelAngles, 0, sizeof( wheelAngles ) );
1770         BecomeActive( TH_THINK );
1771 }
1772
1773 /*
1774 ================
1775 idAFEntity_VehicleSimple::Think
1776 ================
1777 */
1778 void idAFEntity_VehicleSimple::Think( void ) {
1779         int i;
1780         float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
1781         idVec3 origin;
1782         idMat3 axis;
1783         idRotation wheelRotation, steerRotation;
1784
1785         if ( thinkFlags & TH_THINK ) {
1786
1787                 if ( player ) {
1788                         // capture the input from a player
1789                         velocity = g_vehicleVelocity.GetFloat();
1790                         if ( player->usercmd.forwardmove < 0 ) {
1791                                 velocity = -velocity;
1792                         }
1793                         force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
1794                         steerAngle = GetSteerAngle();
1795                 }
1796
1797                 // update the wheel motor force and steering
1798                 for ( i = 0; i < 2; i++ ) {
1799
1800                         // front wheel drive
1801                         if ( velocity != 0.0f ) {
1802                                 suspension[i]->EnableMotor( true );
1803                         } else {
1804                                 suspension[i]->EnableMotor( false );
1805                         }
1806                         suspension[i]->SetMotorVelocity( velocity );
1807                         suspension[i]->SetMotorForce( force );
1808
1809                         // update the wheel steering
1810                         suspension[i]->SetSteerAngle( steerAngle );
1811                 }
1812
1813                 // adjust wheel velocity for better steering because there are no differentials between the wheels
1814                 if ( steerAngle < 0.0f ) {
1815                         suspension[0]->SetMotorVelocity( velocity * 0.5f );
1816                 } else if ( steerAngle > 0.0f ) {
1817                         suspension[1]->SetMotorVelocity( velocity * 0.5f );
1818                 }
1819
1820                 // update suspension with latest cvar settings
1821                 for ( i = 0; i < 4; i++ ) {
1822                         suspension[i]->SetSuspension(   g_vehicleSuspensionUp.GetFloat(),
1823                                                                                         g_vehicleSuspensionDown.GetFloat(),
1824                                                                                         g_vehicleSuspensionKCompress.GetFloat(),
1825                                                                                         g_vehicleSuspensionDamping.GetFloat(),
1826                                                                                         g_vehicleTireFriction.GetFloat() );
1827                 }
1828
1829                 // run the physics
1830                 RunPhysics();
1831
1832                 // move and rotate the wheels visually
1833                 for ( i = 0; i < 4; i++ ) {
1834                         idAFBody *body = af.GetPhysics()->GetBody( 0 );
1835
1836                         origin = suspension[i]->GetWheelOrigin();
1837                         velocity = body->GetPointVelocity( origin ) * body->GetWorldAxis()[0];
1838                         wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
1839
1840                         // additional rotation about the wheel axis
1841                         wheelRotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
1842                         wheelRotation.SetVec( 0, -1, 0 );
1843
1844                         if ( i < 2 ) {
1845                                 // rotate the wheel for steering
1846                                 steerRotation.SetAngle( steerAngle );
1847                                 steerRotation.SetVec( 0, 0, 1 );
1848                                 // set wheel rotation
1849                                 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() * steerRotation.ToMat3() );
1850                         } else {
1851                                 // set wheel rotation
1852                                 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() );
1853                         }
1854
1855                         // set wheel position for suspension
1856                         origin = ( origin - renderEntity.origin ) * renderEntity.axis.Transpose();
1857                         GetAnimator()->SetJointPos( wheelJoints[i], JOINTMOD_WORLD_OVERRIDE, origin );
1858                 }
1859 /*
1860                 // spawn dust particle effects
1861                 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
1862                         int numContacts;
1863                         idAFConstraint_Contact *contacts[2];
1864                         for ( i = 0; i < 4; i++ ) {
1865                                 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
1866                                 for ( int j = 0; j < numContacts; j++ ) {
1867                                         gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
1868                                 }
1869                         }
1870                 }
1871 */
1872         }
1873
1874         UpdateAnimation();
1875         if ( thinkFlags & TH_UPDATEVISUALS ) {
1876                 Present();
1877                 LinkCombat();
1878         }
1879 }
1880
1881
1882 /*
1883 ===============================================================================
1884
1885   idAFEntity_VehicleFourWheels
1886
1887 ===============================================================================
1888 */
1889
1890 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleFourWheels )
1891 END_CLASS
1892
1893
1894 /*
1895 ================
1896 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels
1897 ================
1898 */
1899 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels( void ) {
1900         int i;
1901
1902         for ( i = 0; i < 4; i++ ) {
1903                 wheels[i]               = NULL;
1904                 wheelJoints[i]  = INVALID_JOINT;
1905                 wheelAngles[i]  = 0.0f;
1906         }
1907         steering[0]                     = NULL;
1908         steering[1]                     = NULL;
1909 }
1910
1911 /*
1912 ================
1913 idAFEntity_VehicleFourWheels::Spawn
1914 ================
1915 */
1916 void idAFEntity_VehicleFourWheels::Spawn( void ) {
1917         int i;
1918         static const char *wheelBodyKeys[] = {
1919                 "wheelBodyFrontLeft",
1920                 "wheelBodyFrontRight",
1921                 "wheelBodyRearLeft",
1922                 "wheelBodyRearRight"
1923         };
1924         static const char *wheelJointKeys[] = {
1925                 "wheelJointFrontLeft",
1926                 "wheelJointFrontRight",
1927                 "wheelJointRearLeft",
1928                 "wheelJointRearRight"
1929         };
1930         static const char *steeringHingeKeys[] = {
1931                 "steeringHingeFrontLeft",
1932                 "steeringHingeFrontRight",
1933         };
1934
1935         const char *wheelBodyName, *wheelJointName, *steeringHingeName;
1936
1937         for ( i = 0; i < 4; i++ ) {
1938                 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
1939                 if ( !wheelBodyName[0] ) {
1940                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
1941                 }
1942                 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
1943                 if ( !wheels[i] ) {
1944                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
1945                 }
1946                 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
1947                 if ( !wheelJointName[0] ) {
1948                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
1949                 }
1950                 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
1951                 if ( wheelJoints[i] == INVALID_JOINT ) {
1952                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
1953                 }
1954         }
1955
1956         for ( i = 0; i < 2; i++ ) {
1957                 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
1958                 if ( !steeringHingeName[0] ) {
1959                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
1960                 }
1961                 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
1962                 if ( !steering[i] ) {
1963                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
1964                 }
1965         }
1966
1967         memset( wheelAngles, 0, sizeof( wheelAngles ) );
1968         BecomeActive( TH_THINK );
1969 }
1970
1971 /*
1972 ================
1973 idAFEntity_VehicleFourWheels::Think
1974 ================
1975 */
1976 void idAFEntity_VehicleFourWheels::Think( void ) {
1977         int i;
1978         float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
1979         idVec3 origin;
1980         idMat3 axis;
1981         idRotation rotation;
1982
1983         if ( thinkFlags & TH_THINK ) {
1984
1985                 if ( player ) {
1986                         // capture the input from a player
1987                         velocity = g_vehicleVelocity.GetFloat();
1988                         if ( player->usercmd.forwardmove < 0 ) {
1989                                 velocity = -velocity;
1990                         }
1991                         force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
1992                         steerAngle = GetSteerAngle();
1993                 }
1994
1995                 // update the wheel motor force
1996                 for ( i = 0; i < 2; i++ ) {
1997                         wheels[2+i]->SetContactMotorVelocity( velocity );
1998                         wheels[2+i]->SetContactMotorForce( force );
1999                 }
2000
2001                 // adjust wheel velocity for better steering because there are no differentials between the wheels
2002                 if ( steerAngle < 0.0f ) {
2003                         wheels[2]->SetContactMotorVelocity( velocity * 0.5f );
2004                 }
2005                 else if ( steerAngle > 0.0f ) {
2006                         wheels[3]->SetContactMotorVelocity( velocity * 0.5f );
2007                 }
2008
2009                 // update the wheel steering
2010                 steering[0]->SetSteerAngle( steerAngle );
2011                 steering[1]->SetSteerAngle( steerAngle );
2012                 for ( i = 0; i < 2; i++ ) {
2013                         steering[i]->SetSteerSpeed( 3.0f );
2014                 }
2015
2016                 // update the steering wheel
2017                 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2018                 rotation.SetVec( axis[2] );
2019                 rotation.SetAngle( -steerAngle );
2020                 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2021
2022                 // run the physics
2023                 RunPhysics();
2024
2025                 // rotate the wheels visually
2026                 for ( i = 0; i < 4; i++ ) {
2027                         if ( force == 0.0f ) {
2028                                 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2029                         }
2030                         wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2031                         // give the wheel joint an additional rotation about the wheel axis
2032                         rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2033                         axis = af.GetPhysics()->GetAxis( 0 );
2034                         rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2035                         animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2036                 }
2037
2038                 // spawn dust particle effects
2039                 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2040                         int numContacts;
2041                         idAFConstraint_Contact *contacts[2];
2042                         for ( i = 0; i < 4; i++ ) {
2043                                 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2044                                 for ( int j = 0; j < numContacts; j++ ) {
2045                                         gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
2046                                 }
2047                         }
2048                 }
2049         }
2050
2051         UpdateAnimation();
2052         if ( thinkFlags & TH_UPDATEVISUALS ) {
2053                 Present();
2054                 LinkCombat();
2055         }
2056 }
2057
2058
2059 /*
2060 ===============================================================================
2061
2062   idAFEntity_VehicleSixWheels
2063
2064 ===============================================================================
2065 */
2066
2067 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSixWheels )
2068 END_CLASS
2069
2070         /*
2071 ================
2072 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels
2073 ================
2074 */
2075 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels( void ) {
2076         int i;
2077
2078         for ( i = 0; i < 6; i++ ) {
2079                 wheels[i]               = NULL;
2080                 wheelJoints[i]  = INVALID_JOINT;
2081                 wheelAngles[i]  = 0.0f;
2082         }
2083         steering[0]                     = NULL;
2084         steering[1]                     = NULL;
2085         steering[2]                     = NULL;
2086         steering[3]                     = NULL;
2087 }
2088
2089 /*
2090 ================
2091 idAFEntity_VehicleSixWheels::Spawn
2092 ================
2093 */
2094 void idAFEntity_VehicleSixWheels::Spawn( void ) {
2095         int i;
2096         static const char *wheelBodyKeys[] = {
2097                 "wheelBodyFrontLeft",
2098                 "wheelBodyFrontRight",
2099                 "wheelBodyMiddleLeft",
2100                 "wheelBodyMiddleRight",
2101                 "wheelBodyRearLeft",
2102                 "wheelBodyRearRight"
2103         };
2104         static const char *wheelJointKeys[] = {
2105                 "wheelJointFrontLeft",
2106                 "wheelJointFrontRight",
2107                 "wheelJointMiddleLeft",
2108                 "wheelJointMiddleRight",
2109                 "wheelJointRearLeft",
2110                 "wheelJointRearRight"
2111         };
2112         static const char *steeringHingeKeys[] = {
2113                 "steeringHingeFrontLeft",
2114                 "steeringHingeFrontRight",
2115                 "steeringHingeRearLeft",
2116                 "steeringHingeRearRight"
2117         };
2118
2119         const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2120
2121         for ( i = 0; i < 6; i++ ) {
2122                 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2123                 if ( !wheelBodyName[0] ) {
2124                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2125                 }
2126                 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2127                 if ( !wheels[i] ) {
2128                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2129                 }
2130                 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2131                 if ( !wheelJointName[0] ) {
2132                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2133                 }
2134                 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2135                 if ( wheelJoints[i] == INVALID_JOINT ) {
2136                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2137                 }
2138         }
2139
2140         for ( i = 0; i < 4; i++ ) {
2141                 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2142                 if ( !steeringHingeName[0] ) {
2143                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2144                 }
2145                 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2146                 if ( !steering[i] ) {
2147                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2148                 }
2149         }
2150
2151         memset( wheelAngles, 0, sizeof( wheelAngles ) );
2152         BecomeActive( TH_THINK );
2153 }
2154
2155 /*
2156 ================
2157 idAFEntity_VehicleSixWheels::Think
2158 ================
2159 */
2160 void idAFEntity_VehicleSixWheels::Think( void ) {
2161         int i;
2162         float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2163         idVec3 origin;
2164         idMat3 axis;
2165         idRotation rotation;
2166
2167         if ( thinkFlags & TH_THINK ) {
2168
2169                 if ( player ) {
2170                         // capture the input from a player
2171                         velocity = g_vehicleVelocity.GetFloat();
2172                         if ( player->usercmd.forwardmove < 0 ) {
2173                                 velocity = -velocity;
2174                         }
2175                         force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2176                         steerAngle = GetSteerAngle();
2177                 }
2178
2179                 // update the wheel motor force
2180                 for ( i = 0; i < 6; i++ ) {
2181                         wheels[i]->SetContactMotorVelocity( velocity );
2182                         wheels[i]->SetContactMotorForce( force );
2183                 }
2184
2185                 // adjust wheel velocity for better steering because there are no differentials between the wheels
2186                 if ( steerAngle < 0.0f ) {
2187                         for ( i = 0; i < 3; i++ ) {
2188                                 wheels[(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2189                         }
2190                 }
2191                 else if ( steerAngle > 0.0f ) {
2192                         for ( i = 0; i < 3; i++ ) {
2193                                 wheels[1+(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2194                         }
2195                 }
2196
2197                 // update the wheel steering
2198                 steering[0]->SetSteerAngle( steerAngle );
2199                 steering[1]->SetSteerAngle( steerAngle );
2200                 steering[2]->SetSteerAngle( -steerAngle );
2201                 steering[3]->SetSteerAngle( -steerAngle );
2202                 for ( i = 0; i < 4; i++ ) {
2203                         steering[i]->SetSteerSpeed( 3.0f );
2204                 }
2205
2206                 // update the steering wheel
2207                 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2208                 rotation.SetVec( axis[2] );
2209                 rotation.SetAngle( -steerAngle );
2210                 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2211
2212                 // run the physics
2213                 RunPhysics();
2214
2215                 // rotate the wheels visually
2216                 for ( i = 0; i < 6; i++ ) {
2217                         if ( force == 0.0f ) {
2218                                 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2219                         }
2220                         wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2221                         // give the wheel joint an additional rotation about the wheel axis
2222                         rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2223                         axis = af.GetPhysics()->GetAxis( 0 );
2224                         rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2225                         animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2226                 }
2227
2228                 // spawn dust particle effects
2229                 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2230                         int numContacts;
2231                         idAFConstraint_Contact *contacts[2];
2232                         for ( i = 0; i < 6; i++ ) {
2233                                 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2234                                 for ( int j = 0; j < numContacts; j++ ) {
2235                                         gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
2236                                 }
2237                         }
2238                 }
2239         }
2240
2241         UpdateAnimation();
2242         if ( thinkFlags & TH_UPDATEVISUALS ) {
2243                 Present();
2244                 LinkCombat();
2245         }
2246 }
2247
2248
2249 /*
2250 ===============================================================================
2251
2252   idAFEntity_SteamPipe
2253
2254 ===============================================================================
2255 */
2256
2257 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_SteamPipe )
2258 END_CLASS
2259
2260
2261 /*
2262 ================
2263 idAFEntity_SteamPipe::idAFEntity_SteamPipe
2264 ================
2265 */
2266 idAFEntity_SteamPipe::idAFEntity_SteamPipe( void ) {
2267         steamBody                       = 0;
2268         steamForce                      = 0.0f;
2269         steamUpForce            = 0.0f;
2270         steamModelDefHandle     = -1;
2271         memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2272 }
2273
2274 /*
2275 ================
2276 idAFEntity_SteamPipe::~idAFEntity_SteamPipe
2277 ================
2278 */
2279 idAFEntity_SteamPipe::~idAFEntity_SteamPipe( void ) {
2280         if ( steamModelDefHandle >= 0 ){
2281                 gameRenderWorld->FreeEntityDef( steamModelDefHandle );
2282         }
2283 }
2284
2285 /*
2286 ================
2287 idAFEntity_SteamPipe::Save
2288 ================
2289 */
2290 void idAFEntity_SteamPipe::Save( idSaveGame *savefile ) const {
2291 }
2292
2293 /*
2294 ================
2295 idAFEntity_SteamPipe::Restore
2296 ================
2297 */
2298 void idAFEntity_SteamPipe::Restore( idRestoreGame *savefile ) {
2299         Spawn();
2300 }
2301
2302 /*
2303 ================
2304 idAFEntity_SteamPipe::Spawn
2305 ================
2306 */
2307 void idAFEntity_SteamPipe::Spawn( void ) {
2308         idVec3 steamDir;
2309         const char *steamBodyName;
2310
2311         LoadAF();
2312
2313         SetCombatModel();
2314
2315         SetPhysics( af.GetPhysics() );
2316
2317         fl.takedamage = true;
2318
2319         steamBodyName = spawnArgs.GetString( "steamBody", "" );
2320         steamForce = spawnArgs.GetFloat( "steamForce", "2000" );
2321         steamUpForce = spawnArgs.GetFloat( "steamUpForce", "10" );
2322         steamDir = af.GetPhysics()->GetAxis( steamBody )[2];
2323         steamBody = af.GetPhysics()->GetBodyId( steamBodyName );
2324         force.SetPosition( af.GetPhysics(), steamBody, af.GetPhysics()->GetOrigin( steamBody ) );
2325         force.SetForce( steamDir * -steamForce );
2326
2327         InitSteamRenderEntity();
2328
2329         BecomeActive( TH_THINK );
2330 }
2331
2332 /*
2333 ================
2334 idAFEntity_SteamPipe::InitSteamRenderEntity
2335 ================
2336 */
2337 void idAFEntity_SteamPipe::InitSteamRenderEntity( void ) {
2338         const char      *temp;
2339         const idDeclModelDef *modelDef;
2340
2341         memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2342         steamRenderEntity.shaderParms[ SHADERPARM_RED ]         = 1.0f;
2343         steamRenderEntity.shaderParms[ SHADERPARM_GREEN ]       = 1.0f;
2344         steamRenderEntity.shaderParms[ SHADERPARM_BLUE ]        = 1.0f;
2345         modelDef = NULL;
2346         temp = spawnArgs.GetString ( "model_steam" );
2347         if ( *temp != '\0' ) {
2348                 if ( !strstr( temp, "." ) ) {
2349                         modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, temp, false ) );
2350                         if ( modelDef ) {
2351                                 steamRenderEntity.hModel = modelDef->ModelHandle();
2352                         }
2353                 }
2354
2355                 if ( !steamRenderEntity.hModel ) {
2356                         steamRenderEntity.hModel = renderModelManager->FindModel( temp );
2357                 }
2358
2359                 if ( steamRenderEntity.hModel ) {
2360                         steamRenderEntity.bounds = steamRenderEntity.hModel->Bounds( &steamRenderEntity );
2361                 } else {
2362                         steamRenderEntity.bounds.Zero();
2363                 }
2364                 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2365                 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2366                 steamModelDefHandle = gameRenderWorld->AddEntityDef( &steamRenderEntity );
2367         }
2368 }
2369
2370 /*
2371 ================
2372 idAFEntity_SteamPipe::Think
2373 ================
2374 */
2375 void idAFEntity_SteamPipe::Think( void ) {
2376         idVec3 steamDir;
2377
2378         if ( thinkFlags & TH_THINK ) {
2379                 steamDir.x = gameLocal.random.CRandomFloat() * steamForce;
2380                 steamDir.y = gameLocal.random.CRandomFloat() * steamForce;
2381                 steamDir.z = steamUpForce;
2382                 force.SetForce( steamDir );
2383                 force.Evaluate( gameLocal.time );
2384                 //gameRenderWorld->DebugArrow( colorWhite, af.GetPhysics()->GetOrigin( steamBody ), af.GetPhysics()->GetOrigin( steamBody ) - 10.0f * steamDir, 4 );
2385         }
2386
2387         if ( steamModelDefHandle >= 0 ){
2388                 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2389                 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2390                 gameRenderWorld->UpdateEntityDef( steamModelDefHandle, &steamRenderEntity );
2391         }
2392
2393         idAFEntity_Base::Think();
2394 }
2395
2396
2397 /*
2398 ===============================================================================
2399
2400   idAFEntity_ClawFourFingers
2401
2402 ===============================================================================
2403 */
2404
2405 const idEventDef EV_SetFingerAngle( "setFingerAngle", "f" );
2406 const idEventDef EV_StopFingers( "stopFingers" );
2407
2408 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_ClawFourFingers )
2409         EVENT( EV_SetFingerAngle,               idAFEntity_ClawFourFingers::Event_SetFingerAngle )
2410         EVENT( EV_StopFingers,                  idAFEntity_ClawFourFingers::Event_StopFingers )
2411 END_CLASS
2412
2413 static const char *clawConstraintNames[] = {
2414         "claw1", "claw2", "claw3", "claw4"
2415 };
2416
2417 /*
2418 ================
2419 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers
2420 ================
2421 */
2422 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers( void ) {
2423         fingers[0]      = NULL;
2424         fingers[1]      = NULL;
2425         fingers[2]      = NULL;
2426         fingers[3]      = NULL;
2427 }
2428
2429 /*
2430 ================
2431 idAFEntity_ClawFourFingers::Save
2432 ================
2433 */
2434 void idAFEntity_ClawFourFingers::Save( idSaveGame *savefile ) const {
2435         int i;
2436
2437         for ( i = 0; i < 4; i++ ) {
2438                 fingers[i]->Save( savefile );
2439         }
2440 }
2441
2442 /*
2443 ================
2444 idAFEntity_ClawFourFingers::Restore
2445 ================
2446 */
2447 void idAFEntity_ClawFourFingers::Restore( idRestoreGame *savefile ) {
2448         int i;
2449
2450         for ( i = 0; i < 4; i++ ) {
2451                 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2452                 fingers[i]->Restore( savefile );
2453         }
2454
2455         SetCombatModel();
2456         LinkCombat();
2457 }
2458
2459 /*
2460 ================
2461 idAFEntity_ClawFourFingers::Spawn
2462 ================
2463 */
2464 void idAFEntity_ClawFourFingers::Spawn( void ) {
2465         int i;
2466
2467         LoadAF();
2468
2469         SetCombatModel();
2470
2471         af.GetPhysics()->LockWorldConstraints( true );
2472         af.GetPhysics()->SetForcePushable( true );
2473         SetPhysics( af.GetPhysics() );
2474
2475         fl.takedamage = true;
2476
2477         for ( i = 0; i < 4; i++ ) {
2478                 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2479                 if ( !fingers[i] ) {
2480                         gameLocal.Error( "idClaw_FourFingers '%s': can't find claw constraint '%s'", name.c_str(), clawConstraintNames[i] );
2481                 }
2482         }
2483 }
2484
2485 /*
2486 ================
2487 idAFEntity_ClawFourFingers::Event_SetFingerAngle
2488 ================
2489 */
2490 void idAFEntity_ClawFourFingers::Event_SetFingerAngle( float angle ) {
2491         int i;
2492
2493         for ( i = 0; i < 4; i++ ) {
2494                 fingers[i]->SetSteerAngle( angle );
2495                 fingers[i]->SetSteerSpeed( 0.5f );
2496         }
2497         af.GetPhysics()->Activate();
2498 }
2499
2500 /*
2501 ================
2502 idAFEntity_ClawFourFingers::Event_StopFingers
2503 ================
2504 */
2505 void idAFEntity_ClawFourFingers::Event_StopFingers( void ) {
2506         int i;
2507
2508         for ( i = 0; i < 4; i++ ) {
2509                 fingers[i]->SetSteerAngle( fingers[i]->GetAngle() );
2510         }
2511 }
2512
2513
2514 /*
2515 ===============================================================================
2516
2517   editor support routines
2518
2519 ===============================================================================
2520 */
2521
2522
2523 /*
2524 ================
2525 idGameEdit::AF_SpawnEntity
2526 ================
2527 */
2528 bool idGameEdit::AF_SpawnEntity( const char *fileName ) {
2529         idDict args;
2530         idPlayer *player;
2531         idAFEntity_Generic *ent;
2532         const idDeclAF *af;
2533         idVec3 org;
2534         float yaw;
2535
2536         player = gameLocal.GetLocalPlayer();
2537         if ( !player || !gameLocal.CheatsOk( false ) ) {
2538                 return false;
2539         }
2540
2541         af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, fileName ) );
2542         if ( !af ) {
2543                 return false;
2544         }
2545
2546         yaw = player->viewAngles.yaw;
2547         args.Set( "angle", va( "%f", yaw + 180 ) );
2548         org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
2549         args.Set( "origin", org.ToString() );
2550         args.Set( "spawnclass", "idAFEntity_Generic" );
2551         if ( af->model[0] ) {
2552                 args.Set( "model", af->model.c_str() );
2553         } else {
2554                 args.Set( "model", fileName );
2555         }
2556         if ( af->skin[0] ) {
2557                 args.Set( "skin", af->skin.c_str() );
2558         }
2559         args.Set( "articulatedFigure", fileName );
2560         args.Set( "nodrop", "1" );
2561         ent = static_cast<idAFEntity_Generic *>(gameLocal.SpawnEntityType( idAFEntity_Generic::Type, &args));
2562
2563         // always update this entity
2564         ent->BecomeActive( TH_THINK );
2565         ent->KeepRunningPhysics();
2566         ent->fl.forcePhysicsUpdate = true;
2567
2568         player->dragEntity.SetSelected( ent );
2569
2570         return true;
2571 }
2572
2573 /*
2574 ================
2575 idGameEdit::AF_UpdateEntities
2576 ================
2577 */
2578 void idGameEdit::AF_UpdateEntities( const char *fileName ) {
2579         idEntity *ent;
2580         idAFEntity_Base *af;
2581         idStr name;
2582
2583         name = fileName;
2584         name.StripFileExtension();
2585
2586         // reload any idAFEntity_Generic which uses the given articulated figure file
2587         for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2588                 if ( ent->IsType( idAFEntity_Base::Type ) ) {
2589                         af = static_cast<idAFEntity_Base *>(ent);
2590                         if ( name.Icmp( af->GetAFName() ) == 0 ) {
2591                                 af->LoadAF();
2592                                 af->GetAFPhysics()->PutToRest();
2593                         }
2594                 }
2595         }
2596 }
2597
2598 /*
2599 ================
2600 idGameEdit::AF_UndoChanges
2601 ================
2602 */
2603 void idGameEdit::AF_UndoChanges( void ) {
2604         int i, c;
2605         idEntity *ent;
2606         idAFEntity_Base *af;
2607         idDeclAF *decl;
2608
2609         c = declManager->GetNumDecls( DECL_AF );
2610         for ( i = 0; i < c; i++ ) {
2611                 decl = static_cast<idDeclAF *>( const_cast<idDecl *>( declManager->DeclByIndex( DECL_AF, i, false ) ) );
2612                 if ( !decl->modified ) {
2613                         continue;
2614                 }
2615
2616                 decl->Invalidate();
2617                 declManager->FindType( DECL_AF, decl->GetName() );
2618
2619                 // reload all AF entities using the file
2620                 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2621                         if ( ent->IsType( idAFEntity_Base::Type ) ) {
2622                                 af = static_cast<idAFEntity_Base *>(ent);
2623                                 if ( idStr::Icmp( decl->GetName(), af->GetAFName() ) == 0 ) {
2624                                         af->LoadAF();
2625                                 }
2626                         }
2627                 }
2628         }
2629 }
2630
2631 /*
2632 ================
2633 GetJointTransform
2634 ================
2635 */
2636 typedef struct {
2637         renderEntity_t *ent;
2638         const idMD5Joint *joints;
2639 } jointTransformData_t;
2640
2641 static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
2642         int i;
2643         jointTransformData_t *data = reinterpret_cast<jointTransformData_t *>(model);
2644
2645         for ( i = 0; i < data->ent->numJoints; i++ ) {
2646                 if ( data->joints[i].name.Icmp( jointName ) == 0 ) {
2647                         break;
2648                 }
2649         }
2650         if ( i >= data->ent->numJoints ) {
2651                 return false;
2652         }
2653         origin = frame[i].ToVec3();
2654         axis = frame[i].ToMat3();
2655         return true;
2656 }
2657
2658 /*
2659 ================
2660 GetArgString
2661 ================
2662 */
2663 static const char *GetArgString( const idDict &args, const idDict *defArgs, const char *key ) {
2664         const char *s;
2665
2666         s = args.GetString( key );
2667         if ( !s[0] && defArgs ) {
2668                 s = defArgs->GetString( key );
2669         }
2670         return s;
2671 }
2672
2673 /*
2674 ================
2675 idGameEdit::AF_CreateMesh
2676 ================
2677 */
2678 idRenderModel *idGameEdit::AF_CreateMesh( const idDict &args, idVec3 &meshOrigin, idMat3 &meshAxis, bool &poseIsSet ) {
2679         int i, jointNum;
2680         const idDeclAF *af;
2681         const idDeclAF_Body *fb;
2682         renderEntity_t ent;
2683         idVec3 origin, *bodyOrigin, *newBodyOrigin, *modifiedOrigin;
2684         idMat3 axis, *bodyAxis, *newBodyAxis, *modifiedAxis;
2685         declAFJointMod_t *jointMod;
2686         idAngles angles;
2687         const idDict *defArgs;
2688         const idKeyValue *arg;
2689         idStr name;
2690         jointTransformData_t data;
2691         const char *classname, *afName, *modelName;
2692         idRenderModel *md5;
2693         const idDeclModelDef *modelDef;
2694         const idMD5Anim *MD5anim;
2695         const idMD5Joint *MD5joint;
2696         const idMD5Joint *MD5joints;
2697         int numMD5joints;
2698         idJointMat *originalJoints;
2699         int parentNum;
2700
2701         poseIsSet = false;
2702         meshOrigin.Zero();
2703         meshAxis.Identity();
2704
2705         classname = args.GetString( "classname" );
2706         defArgs = gameLocal.FindEntityDefDict( classname );
2707
2708         // get the articulated figure
2709         afName = GetArgString( args, defArgs, "articulatedFigure" );
2710         af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, afName ) );
2711         if ( !af ) {
2712                 return NULL;
2713         }
2714
2715         // get the md5 model
2716         modelName = GetArgString( args, defArgs, "model" );
2717         modelDef = static_cast< const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
2718         if ( !modelDef ) {
2719                 return NULL;
2720         }
2721
2722         // make sure model hasn't been purged
2723         if ( modelDef->ModelHandle() && !modelDef->ModelHandle()->IsLoaded() ) {
2724                 modelDef->ModelHandle()->LoadModel();
2725         }
2726
2727         // get the md5
2728         md5 = modelDef->ModelHandle();
2729         if ( !md5 || md5->IsDefaultModel() ) {
2730                 return NULL;
2731         }
2732
2733         // get the articulated figure pose anim
2734         int animNum = modelDef->GetAnim( "af_pose" );
2735         if ( !animNum ) {
2736                 return NULL;
2737         }
2738         const idAnim *anim = modelDef->GetAnim( animNum );
2739         if ( !anim ) {
2740                 return NULL;
2741         }
2742         MD5anim = anim->MD5Anim( 0 );
2743         MD5joints = md5->GetJoints();
2744         numMD5joints = md5->NumJoints();
2745
2746         // setup a render entity
2747         memset( &ent, 0, sizeof( ent ) );
2748         ent.customSkin = modelDef->GetSkin();
2749         ent.bounds.Clear();
2750         ent.numJoints = numMD5joints;
2751         ent.joints = ( idJointMat * )_alloca16( ent.numJoints * sizeof( *ent.joints ) );
2752
2753         // create animation from of the af_pose
2754         ANIM_CreateAnimFrame( md5, MD5anim, ent.numJoints, ent.joints, 1, modelDef->GetVisualOffset(), false );
2755
2756         // buffers to store the initial origin and axis for each body
2757         bodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
2758         bodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
2759         newBodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
2760         newBodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
2761
2762         // finish the AF positions
2763         data.ent = &ent;
2764         data.joints = MD5joints;
2765         af->Finish( GetJointTransform, ent.joints, &data );
2766
2767         // get the initial origin and axis for each AF body
2768         for ( i = 0; i < af->bodies.Num(); i++ ) {
2769                 fb = af->bodies[i];
2770
2771                 if ( fb->modelType == TRM_BONE ) {
2772                         // axis of bone trace model
2773                         axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
2774                         axis[2].Normalize();
2775                         axis[2].NormalVectors( axis[0], axis[1] );
2776                         axis[1] = -axis[1];
2777                 } else {
2778                         axis = fb->angles.ToMat3();
2779                 }
2780
2781                 newBodyOrigin[i] = bodyOrigin[i] = fb->origin.ToVec3();
2782                 newBodyAxis[i] = bodyAxis[i] = axis;
2783         }
2784
2785         // get any new body transforms stored in the key/value pairs
2786         for ( arg = args.MatchPrefix( "body ", NULL ); arg; arg = args.MatchPrefix( "body ", arg ) ) {
2787                 name = arg->GetKey();
2788                 name.Strip( "body " );
2789                 for ( i = 0; i < af->bodies.Num(); i++ ) {
2790                         fb = af->bodies[i];
2791                         if ( fb->name.Icmp( name ) == 0 ) {
2792                                 break;
2793                         }
2794                 }
2795                 if ( i >= af->bodies.Num() ) {
2796                         continue;
2797                 }
2798                 sscanf( arg->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
2799
2800                 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
2801                         meshAxis = bodyAxis[i].Transpose() * angles.ToMat3();
2802                         meshOrigin = origin - bodyOrigin[i] * meshAxis;
2803                         poseIsSet = true;
2804                 } else {
2805                         newBodyOrigin[i] = origin;
2806                         newBodyAxis[i] = angles.ToMat3();
2807                 }
2808         }
2809
2810         // save the original joints
2811         originalJoints = ( idJointMat * )_alloca16( numMD5joints * sizeof( originalJoints[0] ) );
2812         memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) );
2813
2814         // buffer to store the joint mods
2815         jointMod = (declAFJointMod_t *) _alloca16( numMD5joints * sizeof( declAFJointMod_t ) );
2816         memset( jointMod, -1, numMD5joints * sizeof( declAFJointMod_t ) );
2817         modifiedOrigin = (idVec3 *) _alloca16( numMD5joints * sizeof( idVec3 ) );
2818         memset( modifiedOrigin, 0, numMD5joints * sizeof( idVec3 ) );
2819         modifiedAxis = (idMat3 *) _alloca16( numMD5joints * sizeof( idMat3 ) );
2820         memset( modifiedAxis, 0, numMD5joints * sizeof( idMat3 ) );
2821
2822         // get all the joint modifications
2823         for ( i = 0; i < af->bodies.Num(); i++ ) {
2824                 fb = af->bodies[i];
2825
2826                 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
2827                         continue;
2828                 }
2829
2830                 for ( jointNum = 0; jointNum < numMD5joints; jointNum++ ) {
2831                         if ( MD5joints[jointNum].name.Icmp( fb->jointName ) == 0 ) {
2832                                 break;
2833                         }
2834                 }
2835
2836                 if ( jointNum >= 0 && jointNum < ent.numJoints ) {
2837                         jointMod[ jointNum ] = fb->jointMod;
2838                         modifiedAxis[ jointNum ] = ( bodyAxis[i] * originalJoints[jointNum].ToMat3().Transpose() ).Transpose() * ( newBodyAxis[i] * meshAxis.Transpose() );
2839                         // FIXME: calculate correct modifiedOrigin
2840                         modifiedOrigin[ jointNum ] = originalJoints[ jointNum ].ToVec3();
2841                 }
2842         }
2843
2844         // apply joint modifications to the skeleton
2845         MD5joint = MD5joints + 1;
2846         for( i = 1; i < numMD5joints; i++, MD5joint++ ) {
2847
2848                 parentNum = MD5joint->parent - MD5joints;
2849                 idMat3 parentAxis = originalJoints[ parentNum ].ToMat3();
2850                 idMat3 localm = originalJoints[i].ToMat3() * parentAxis.Transpose();
2851                 idVec3 localt = ( originalJoints[i].ToVec3() - originalJoints[ parentNum ].ToVec3() ) * parentAxis.Transpose();
2852
2853                 switch( jointMod[i] ) {
2854                         case DECLAF_JOINTMOD_ORIGIN: {
2855                                 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
2856                                 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
2857                                 break;
2858                         }
2859                         case DECLAF_JOINTMOD_AXIS: {
2860                                 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
2861                                 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
2862                                 break;
2863                         }
2864                         case DECLAF_JOINTMOD_BOTH: {
2865                                 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
2866                                 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
2867                                 break;
2868                         }
2869                         default: {
2870                                 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
2871                                 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
2872                                 break;
2873                         }
2874                 }
2875         }
2876
2877         // instantiate a mesh using the joint information from the render entity
2878         return md5->InstantiateDynamicModel( &ent, NULL, NULL );
2879 }