]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/AFEntity.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / d3xp / 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 #ifdef _D3XP
964         wasThrown = false;
965 #endif
966 }
967
968 /*
969 ================
970 idAFEntity_Gibbable::~idAFEntity_Gibbable
971 ================
972 */
973 idAFEntity_Gibbable::~idAFEntity_Gibbable() {
974         if ( skeletonModelDefHandle != -1 ) {
975                 gameRenderWorld->FreeEntityDef( skeletonModelDefHandle );
976                 skeletonModelDefHandle = -1;
977         }
978 }
979
980 /*
981 ================
982 idAFEntity_Gibbable::Save
983 ================
984 */
985 void idAFEntity_Gibbable::Save( idSaveGame *savefile ) const {
986         savefile->WriteBool( gibbed );
987         savefile->WriteBool( combatModel != NULL );
988 #ifdef _D3XP
989         savefile->WriteBool( wasThrown );
990 #endif
991 }
992
993 /*
994 ================
995 idAFEntity_Gibbable::Restore
996 ================
997 */
998 void idAFEntity_Gibbable::Restore( idRestoreGame *savefile ) {
999         bool hasCombatModel;
1000
1001         savefile->ReadBool( gibbed );
1002         savefile->ReadBool( hasCombatModel );
1003 #ifdef _D3XP
1004         savefile->ReadBool( wasThrown );
1005 #endif
1006
1007         InitSkeletonModel();
1008
1009         if ( hasCombatModel ) {
1010                 SetCombatModel();
1011                 LinkCombat();
1012         }
1013 }
1014
1015 /*
1016 ================
1017 idAFEntity_Gibbable::Spawn
1018 ================
1019 */
1020 void idAFEntity_Gibbable::Spawn( void ) {
1021         InitSkeletonModel();
1022
1023         gibbed = false;
1024 #ifdef _D3XP
1025         wasThrown = false;
1026 #endif
1027 }
1028
1029 /*
1030 ================
1031 idAFEntity_Gibbable::InitSkeletonModel
1032 ================
1033 */
1034 void idAFEntity_Gibbable::InitSkeletonModel( void ) {
1035         const char *modelName;
1036         const idDeclModelDef *modelDef;
1037
1038         skeletonModel = NULL;
1039         skeletonModelDefHandle = -1;
1040
1041         modelName = spawnArgs.GetString( "model_gib" );
1042
1043         modelDef = NULL;
1044         if ( modelName[0] != '\0' ) {
1045                 modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
1046                 if ( modelDef ) {
1047                         skeletonModel = modelDef->ModelHandle();
1048                 } else {
1049                         skeletonModel = renderModelManager->FindModel( modelName );
1050                 }
1051                 if ( skeletonModel != NULL && renderEntity.hModel != NULL ) {
1052                         if ( skeletonModel->NumJoints() != renderEntity.hModel->NumJoints() ) {
1053                                 gameLocal.Error( "gib model '%s' has different number of joints than model '%s'",
1054                                                                         skeletonModel->Name(), renderEntity.hModel->Name() );
1055                         }
1056                 }
1057         }
1058 }
1059
1060 /*
1061 ================
1062 idAFEntity_Gibbable::Present
1063 ================
1064 */
1065 void idAFEntity_Gibbable::Present( void ) {
1066         renderEntity_t skeleton;
1067
1068         if ( !gameLocal.isNewFrame ) {
1069                 return;
1070         }
1071
1072         // don't present to the renderer if the entity hasn't changed
1073         if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
1074                 return;
1075         }
1076
1077         // update skeleton model
1078         if ( gibbed && !IsHidden() && skeletonModel != NULL ) {
1079                 skeleton = renderEntity;
1080                 skeleton.hModel = skeletonModel;
1081                 // add to refresh list
1082                 if ( skeletonModelDefHandle == -1 ) {
1083                         skeletonModelDefHandle = gameRenderWorld->AddEntityDef( &skeleton );
1084                 } else {
1085                         gameRenderWorld->UpdateEntityDef( skeletonModelDefHandle, &skeleton );
1086                 }
1087         }
1088
1089         idEntity::Present();
1090 }
1091
1092 /*
1093 ================
1094 idAFEntity_Gibbable::Damage
1095 ================
1096 */
1097 void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
1098         if ( !fl.takedamage ) {
1099                 return;
1100         }
1101         idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
1102         if ( health < -20 && spawnArgs.GetBool( "gib" ) ) {
1103                 Gib( dir, damageDefName );
1104         }
1105 }
1106
1107 #ifdef _D3XP
1108 /*
1109 =====================
1110 idAFEntity_Gibbable::SetThrown
1111 =====================
1112 */
1113 void idAFEntity_Gibbable::SetThrown( bool isThrown ) {
1114
1115         if ( isThrown ) {
1116                 int i, num = af.GetPhysics()->GetNumBodies();
1117
1118                 for ( i=0; i<num; i++ ) {
1119                         idAFBody *body;
1120
1121                         body = af.GetPhysics()->GetBody( i );
1122                         body->SetClipMask( MASK_MONSTERSOLID );
1123                 }
1124         }
1125
1126         wasThrown = isThrown;
1127 }
1128
1129 /*
1130 =====================
1131 idAFEntity_Gibbable::Collide
1132 =====================
1133 */
1134 bool idAFEntity_Gibbable::Collide( const trace_t &collision, const idVec3 &velocity ) {
1135
1136         if ( !gibbed && wasThrown ) {
1137
1138                 // Everything gibs (if possible)
1139                 if ( spawnArgs.GetBool( "gib" ) ) {
1140                         idEntity        *ent;
1141
1142                         ent = gameLocal.entities[ collision.c.entityNum ];
1143                         if ( ent->fl.takedamage ) {
1144                                 ent->Damage( this, gameLocal.GetLocalPlayer(), collision.c.normal, "damage_thrown_ragdoll", 1.f, CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) );
1145                         }
1146
1147                         idVec3 vel = velocity;
1148                         vel.NormalizeFast();
1149                         Gib( vel, "damage_gib" );
1150                 }
1151         }
1152
1153         return idAFEntity_Base::Collide( collision, velocity );
1154 }
1155 #endif
1156
1157 /*
1158 =====================
1159 idAFEntity_Gibbable::SpawnGibs
1160 =====================
1161 */
1162 void idAFEntity_Gibbable::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
1163         int i;
1164         bool gibNonSolid;
1165         idVec3 entityCenter, velocity;
1166         idList<idEntity *> list;
1167
1168         assert( !gameLocal.isClient );
1169
1170         const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1171         if ( !damageDef ) {
1172                 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1173         }
1174
1175         // spawn gib articulated figures
1176         idAFEntity_Base::DropAFs( this, "gib", &list );
1177
1178         // spawn gib items
1179         idMoveableItem::DropItems( this, "gib", &list );
1180
1181         // blow out the gibs in the given direction away from the center of the entity
1182         entityCenter = GetPhysics()->GetAbsBounds().GetCenter();
1183         gibNonSolid = damageDef->GetBool( "gibNonSolid" );
1184         for ( i = 0; i < list.Num(); i++ ) {
1185                 if ( gibNonSolid ) {
1186                         list[i]->GetPhysics()->SetContents( 0 );
1187                         list[i]->GetPhysics()->SetClipMask( 0 );
1188                         list[i]->GetPhysics()->UnlinkClip();
1189                         list[i]->GetPhysics()->PutToRest();
1190                 } else {
1191 #ifdef _D3XP
1192                         list[i]->GetPhysics()->SetContents( 0 );
1193 #else
1194                         list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE );
1195 #endif
1196                         list[i]->GetPhysics()->SetClipMask( CONTENTS_SOLID );
1197                         velocity = list[i]->GetPhysics()->GetAbsBounds().GetCenter() - entityCenter;
1198                         velocity.NormalizeFast();
1199                         velocity += ( i & 1 ) ? dir : -dir;
1200                         list[i]->GetPhysics()->SetLinearVelocity( velocity * 75.0f );
1201                 }
1202 #ifdef _D3XP
1203                 // Don't allow grabber to pick up temporary gibs
1204                 list[i]->noGrab = true;
1205 #endif
1206                 list[i]->GetRenderEntity()->noShadow = true;
1207                 list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1208                 list[i]->PostEventSec( &EV_Remove, 4.0f );
1209         }
1210 }
1211
1212 /*
1213 ============
1214 idAFEntity_Gibbable::Gib
1215 ============
1216 */
1217 void idAFEntity_Gibbable::Gib( const idVec3 &dir, const char *damageDefName ) {
1218         // only gib once
1219         if ( gibbed ) {
1220                 return;
1221         }
1222
1223 #ifdef _D3XP
1224         // Don't grab this ent after it's been gibbed (and now invisible!)
1225         noGrab = true;
1226 #endif
1227
1228         const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1229         if ( !damageDef ) {
1230                 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1231         }
1232
1233         if ( damageDef->GetBool( "gibNonSolid" ) ) {
1234                 GetAFPhysics()->SetContents( 0 );
1235                 GetAFPhysics()->SetClipMask( 0 );
1236                 GetAFPhysics()->UnlinkClip();
1237                 GetAFPhysics()->PutToRest();
1238         } else {
1239                 GetAFPhysics()->SetContents( CONTENTS_CORPSE );
1240                 GetAFPhysics()->SetClipMask( CONTENTS_SOLID );
1241         }
1242
1243         UnlinkCombat();
1244
1245         if ( g_bloodEffects.GetBool() ) {
1246                 if ( gameLocal.time > gameLocal.GetGibTime() ) {
1247                         gameLocal.SetGibTime( gameLocal.time + GIB_DELAY );
1248                         SpawnGibs( dir, damageDefName );
1249                         renderEntity.noShadow = true;
1250                         renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1251                         StartSound( "snd_gibbed", SND_CHANNEL_ANY, 0, false, NULL );
1252                         gibbed = true;
1253                 }
1254         } else {
1255                 gibbed = true;
1256         }
1257
1258
1259         PostEventSec( &EV_Gibbed, 4.0f );
1260 }
1261
1262 /*
1263 ============
1264 idAFEntity_Gibbable::Event_Gib
1265 ============
1266 */
1267 void idAFEntity_Gibbable::Event_Gib( const char *damageDefName ) {
1268         Gib( idVec3( 0, 0, 1 ), damageDefName );
1269 }
1270
1271 /*
1272 ===============================================================================
1273
1274   idAFEntity_Generic
1275
1276 ===============================================================================
1277 */
1278
1279 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_Generic )
1280         EVENT( EV_Activate,                     idAFEntity_Generic::Event_Activate )
1281 END_CLASS
1282
1283 /*
1284 ================
1285 idAFEntity_Generic::idAFEntity_Generic
1286 ================
1287 */
1288 idAFEntity_Generic::idAFEntity_Generic( void ) {
1289         keepRunningPhysics = false;
1290 }
1291
1292 /*
1293 ================
1294 idAFEntity_Generic::~idAFEntity_Generic
1295 ================
1296 */
1297 idAFEntity_Generic::~idAFEntity_Generic( void ) {
1298 }
1299
1300 /*
1301 ================
1302 idAFEntity_Generic::Save
1303 ================
1304 */
1305 void idAFEntity_Generic::Save( idSaveGame *savefile ) const {
1306         savefile->WriteBool( keepRunningPhysics );
1307 }
1308
1309 /*
1310 ================
1311 idAFEntity_Generic::Restore
1312 ================
1313 */
1314 void idAFEntity_Generic::Restore( idRestoreGame *savefile ) {
1315         savefile->ReadBool( keepRunningPhysics );
1316 }
1317
1318 /*
1319 ================
1320 idAFEntity_Generic::Think
1321 ================
1322 */
1323 void idAFEntity_Generic::Think( void ) {
1324         idAFEntity_Base::Think();
1325
1326         if ( keepRunningPhysics ) {
1327                 BecomeActive( TH_PHYSICS );
1328         }
1329 }
1330
1331 /*
1332 ================
1333 idAFEntity_Generic::Spawn
1334 ================
1335 */
1336 void idAFEntity_Generic::Spawn( void ) {
1337         if ( !LoadAF() ) {
1338                 gameLocal.Error( "Couldn't load af file on entity '%s'", name.c_str() );
1339         }
1340
1341         SetCombatModel();
1342
1343         SetPhysics( af.GetPhysics() );
1344
1345         af.GetPhysics()->PutToRest();
1346         if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1347                 af.GetPhysics()->Activate();
1348         }
1349
1350         fl.takedamage = true;
1351 }
1352
1353 /*
1354 ================
1355 idAFEntity_Generic::Event_Activate
1356 ================
1357 */
1358 void idAFEntity_Generic::Event_Activate( idEntity *activator ) {
1359         float delay;
1360         idVec3 init_velocity, init_avelocity;
1361
1362         Show();
1363
1364         af.GetPhysics()->EnableImpact();
1365         af.GetPhysics()->Activate();
1366
1367         spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1368         spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1369
1370         delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1371         if ( delay == 0.0f ) {
1372                 af.GetPhysics()->SetLinearVelocity( init_velocity );
1373         } else {
1374                 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1375         }
1376
1377         delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1378         if ( delay == 0.0f ) {
1379                 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1380         } else {
1381                 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1382         }
1383 }
1384
1385
1386 /*
1387 ===============================================================================
1388
1389   idAFEntity_WithAttachedHead
1390
1391 ===============================================================================
1392 */
1393
1394 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_WithAttachedHead )
1395         EVENT( EV_Gib,                          idAFEntity_WithAttachedHead::Event_Gib )
1396         EVENT( EV_Activate,                     idAFEntity_WithAttachedHead::Event_Activate )
1397 END_CLASS
1398
1399 /*
1400 ================
1401 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead
1402 ================
1403 */
1404 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead() {
1405         head = NULL;
1406 }
1407
1408 /*
1409 ================
1410 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead
1411 ================
1412 */
1413 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead() {
1414         if ( head.GetEntity() ) {
1415                 head.GetEntity()->ClearBody();
1416                 head.GetEntity()->PostEventMS( &EV_Remove, 0 );
1417         }
1418 }
1419
1420 /*
1421 ================
1422 idAFEntity_WithAttachedHead::Spawn
1423 ================
1424 */
1425 void idAFEntity_WithAttachedHead::Spawn( void ) {
1426         SetupHead();
1427
1428         LoadAF();
1429
1430         SetCombatModel();
1431
1432         SetPhysics( af.GetPhysics() );
1433
1434         af.GetPhysics()->PutToRest();
1435         if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1436                 af.GetPhysics()->Activate();
1437         }
1438
1439         fl.takedamage = true;
1440
1441         if ( head.GetEntity() ) {
1442                 int anim = head.GetEntity()->GetAnimator()->GetAnim( "dead" );
1443
1444                 if ( anim ) {
1445                         head.GetEntity()->GetAnimator()->SetFrame( ANIMCHANNEL_ALL, anim, 0, gameLocal.time, 0 );
1446                 }
1447         }
1448 }
1449
1450 /*
1451 ================
1452 idAFEntity_WithAttachedHead::Save
1453 ================
1454 */
1455 void idAFEntity_WithAttachedHead::Save( idSaveGame *savefile ) const {
1456         head.Save( savefile );
1457 }
1458
1459 /*
1460 ================
1461 idAFEntity_WithAttachedHead::Restore
1462 ================
1463 */
1464 void idAFEntity_WithAttachedHead::Restore( idRestoreGame *savefile ) {
1465         head.Restore( savefile );
1466 }
1467
1468 /*
1469 ================
1470 idAFEntity_WithAttachedHead::SetupHead
1471 ================
1472 */
1473 void idAFEntity_WithAttachedHead::SetupHead( void ) {
1474         idAFAttachment          *headEnt;
1475         idStr                           jointName;
1476         const char                      *headModel;
1477         jointHandle_t           joint;
1478         idVec3                          origin;
1479         idMat3                          axis;
1480
1481         headModel = spawnArgs.GetString( "def_head", "" );
1482         if ( headModel[ 0 ] ) {
1483                 jointName = spawnArgs.GetString( "head_joint" );
1484                 joint = animator.GetJointHandle( jointName );
1485                 if ( joint == INVALID_JOINT ) {
1486                         gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
1487                 }
1488
1489                 headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, NULL ) );
1490                 headEnt->SetName( va( "%s_head", name.c_str() ) );
1491                 headEnt->SetBody( this, headModel, joint );
1492                 headEnt->SetCombatModel();
1493                 head = headEnt;
1494
1495 #ifdef _D3XP
1496                 idStr xSkin;
1497                 if ( spawnArgs.GetString( "skin_head_xray", "", xSkin ) ) {
1498                         headEnt->xraySkin = declManager->FindSkin( xSkin.c_str() );
1499                         headEnt->UpdateModel();
1500                 }
1501 #endif
1502                 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
1503                 origin = renderEntity.origin + origin * renderEntity.axis;
1504                 headEnt->SetOrigin( origin );
1505                 headEnt->SetAxis( renderEntity.axis );
1506                 headEnt->BindToJoint( this, joint, true );
1507         }
1508 }
1509
1510 /*
1511 ================
1512 idAFEntity_WithAttachedHead::Think
1513 ================
1514 */
1515 void idAFEntity_WithAttachedHead::Think( void ) {
1516         idAFEntity_Base::Think();
1517 }
1518
1519 /*
1520 ================
1521 idAFEntity_WithAttachedHead::LinkCombat
1522 ================
1523 */
1524 void idAFEntity_WithAttachedHead::LinkCombat( void ) {
1525         idAFAttachment *headEnt;
1526
1527         if ( fl.hidden ) {
1528                 return;
1529         }
1530
1531         if ( combatModel ) {
1532                 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1533         }
1534         headEnt = head.GetEntity();
1535         if ( headEnt ) {
1536                 headEnt->LinkCombat();
1537         }
1538 }
1539
1540 /*
1541 ================
1542 idAFEntity_WithAttachedHead::UnlinkCombat
1543 ================
1544 */
1545 void idAFEntity_WithAttachedHead::UnlinkCombat( void ) {
1546         idAFAttachment *headEnt;
1547
1548         if ( combatModel ) {
1549                 combatModel->Unlink();
1550         }
1551         headEnt = head.GetEntity();
1552         if ( headEnt ) {
1553                 headEnt->UnlinkCombat();
1554         }
1555 }
1556
1557 /*
1558 ================
1559 idAFEntity_WithAttachedHead::Hide
1560 ================
1561 */
1562 void idAFEntity_WithAttachedHead::Hide( void ) {
1563         idAFEntity_Base::Hide();
1564         if ( head.GetEntity() ) {
1565                 head.GetEntity()->Hide();
1566         }
1567         UnlinkCombat();
1568 }
1569
1570 /*
1571 ================
1572 idAFEntity_WithAttachedHead::Show
1573 ================
1574 */
1575 void idAFEntity_WithAttachedHead::Show( void ) {
1576         idAFEntity_Base::Show();
1577         if ( head.GetEntity() ) {
1578                 head.GetEntity()->Show();
1579         }
1580         LinkCombat();
1581 }
1582
1583 /*
1584 ================
1585 idAFEntity_WithAttachedHead::ProjectOverlay
1586 ================
1587 */
1588 void idAFEntity_WithAttachedHead::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1589
1590         idEntity::ProjectOverlay( origin, dir, size, material );
1591
1592         if ( head.GetEntity() ) {
1593                 head.GetEntity()->ProjectOverlay( origin, dir, size, material );
1594         }
1595 }
1596
1597 /*
1598 ============
1599 idAFEntity_WithAttachedHead::Gib
1600 ============
1601 */
1602 void idAFEntity_WithAttachedHead::Gib( const idVec3 &dir, const char *damageDefName ) {
1603         // only gib once
1604         if ( gibbed ) {
1605                 return;
1606         }
1607         idAFEntity_Gibbable::Gib( dir, damageDefName );
1608         if ( head.GetEntity() ) {
1609                 head.GetEntity()->Hide();
1610         }
1611 }
1612
1613 /*
1614 ============
1615 idAFEntity_WithAttachedHead::Event_Gib
1616 ============
1617 */
1618 void idAFEntity_WithAttachedHead::Event_Gib( const char *damageDefName ) {
1619         Gib( idVec3( 0, 0, 1 ), damageDefName );
1620 }
1621
1622 /*
1623 ================
1624 idAFEntity_WithAttachedHead::Event_Activate
1625 ================
1626 */
1627 void idAFEntity_WithAttachedHead::Event_Activate( idEntity *activator ) {
1628         float delay;
1629         idVec3 init_velocity, init_avelocity;
1630
1631         Show();
1632
1633         af.GetPhysics()->EnableImpact();
1634         af.GetPhysics()->Activate();
1635
1636         spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1637         spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1638
1639         delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1640         if ( delay == 0.0f ) {
1641                 af.GetPhysics()->SetLinearVelocity( init_velocity );
1642         } else {
1643                 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1644         }
1645
1646         delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1647         if ( delay == 0.0f ) {
1648                 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1649         } else {
1650                 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1651         }
1652 }
1653
1654
1655 /*
1656 ===============================================================================
1657
1658   idAFEntity_Vehicle
1659
1660 ===============================================================================
1661 */
1662
1663 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Vehicle )
1664 END_CLASS
1665
1666 /*
1667 ================
1668 idAFEntity_Vehicle::idAFEntity_Vehicle
1669 ================
1670 */
1671 idAFEntity_Vehicle::idAFEntity_Vehicle( void ) {
1672         player                          = NULL;
1673         eyesJoint                       = INVALID_JOINT;
1674         steeringWheelJoint      = INVALID_JOINT;
1675         wheelRadius                     = 0.0f;
1676         steerAngle                      = 0.0f;
1677         steerSpeed                      = 0.0f;
1678         dustSmoke                       = NULL;
1679 }
1680
1681 /*
1682 ================
1683 idAFEntity_Vehicle::Spawn
1684 ================
1685 */
1686 void idAFEntity_Vehicle::Spawn( void ) {
1687         const char *eyesJointName = spawnArgs.GetString( "eyesJoint", "eyes" );
1688         const char *steeringWheelJointName = spawnArgs.GetString( "steeringWheelJoint", "steeringWheel" );
1689
1690         LoadAF();
1691
1692         SetCombatModel();
1693
1694         SetPhysics( af.GetPhysics() );
1695
1696         fl.takedamage = true;
1697
1698         if ( !eyesJointName[0] ) {
1699                 gameLocal.Error( "idAFEntity_Vehicle '%s' no eyes joint specified", name.c_str() );
1700         }
1701         eyesJoint = animator.GetJointHandle( eyesJointName );
1702         if ( !steeringWheelJointName[0] ) {
1703                 gameLocal.Error( "idAFEntity_Vehicle '%s' no steering wheel joint specified", name.c_str() );
1704         }
1705         steeringWheelJoint = animator.GetJointHandle( steeringWheelJointName );
1706
1707         spawnArgs.GetFloat( "wheelRadius", "20", wheelRadius );
1708         spawnArgs.GetFloat( "steerSpeed", "5", steerSpeed ); 
1709
1710         player = NULL;
1711         steerAngle = 0.0f;
1712
1713         const char *smokeName = spawnArgs.GetString( "smoke_vehicle_dust", "muzzlesmoke" );
1714         if ( *smokeName != '\0' ) {
1715                 dustSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1716         }
1717 }
1718
1719 /*
1720 ================
1721 idAFEntity_Vehicle::Use
1722 ================
1723 */
1724 void idAFEntity_Vehicle::Use( idPlayer *other ) {
1725         idVec3 origin;
1726         idMat3 axis;
1727
1728         if ( player ) {
1729                 if ( player == other ) {
1730                         other->Unbind();
1731                         player = NULL;
1732
1733                         af.GetPhysics()->SetComeToRest( true );
1734                 }
1735         }
1736         else {
1737                 player = other;
1738                 animator.GetJointTransform( eyesJoint, gameLocal.time, origin, axis );
1739                 origin = renderEntity.origin + origin * renderEntity.axis;
1740                 player->GetPhysics()->SetOrigin( origin );
1741                 player->BindToBody( this, 0, true );
1742
1743                 af.GetPhysics()->SetComeToRest( false );
1744                 af.GetPhysics()->Activate();
1745         }
1746 }
1747
1748 /*
1749 ================
1750 idAFEntity_Vehicle::GetSteerAngle
1751 ================
1752 */
1753 float idAFEntity_Vehicle::GetSteerAngle( void ) {
1754         float idealSteerAngle, angleDelta;
1755
1756         idealSteerAngle = player->usercmd.rightmove * ( 30.0f / 128.0f );
1757         angleDelta = idealSteerAngle - steerAngle;
1758
1759         if ( angleDelta > steerSpeed ) {
1760                 steerAngle += steerSpeed;
1761         } else if ( angleDelta < -steerSpeed ) {
1762                 steerAngle -= steerSpeed;
1763         } else {
1764                 steerAngle = idealSteerAngle;
1765         }
1766
1767         return steerAngle;
1768 }
1769
1770
1771 /*
1772 ===============================================================================
1773
1774   idAFEntity_VehicleSimple
1775
1776 ===============================================================================
1777 */
1778
1779 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSimple )
1780 END_CLASS
1781
1782 /*
1783 ================
1784 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple
1785 ================
1786 */
1787 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple( void ) {
1788         int i;
1789         for ( i = 0; i < 4; i++ ) {
1790                 suspension[i] = NULL;
1791         }
1792 }
1793
1794 /*
1795 ================
1796 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple
1797 ================
1798 */
1799 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple( void ) {
1800         delete wheelModel;
1801         wheelModel = NULL;
1802 }
1803
1804 /*
1805 ================
1806 idAFEntity_VehicleSimple::Spawn
1807 ================
1808 */
1809 void idAFEntity_VehicleSimple::Spawn( void ) {
1810         static const char *wheelJointKeys[] = {
1811                 "wheelJointFrontLeft",
1812                 "wheelJointFrontRight",
1813                 "wheelJointRearLeft",
1814                 "wheelJointRearRight"
1815         };
1816         static idVec3 wheelPoly[4] = { idVec3( 2, 2, 0 ), idVec3( 2, -2, 0 ), idVec3( -2, -2, 0 ), idVec3( -2, 2, 0 ) };
1817
1818         int i;
1819         idVec3 origin;
1820         idMat3 axis;
1821         idTraceModel trm;
1822
1823         trm.SetupPolygon( wheelPoly, 4 );
1824         trm.Translate( idVec3( 0, 0, -wheelRadius ) );
1825         wheelModel = new idClipModel( trm );
1826
1827         for ( i = 0; i < 4; i++ ) {
1828                 const char *wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
1829                 if ( !wheelJointName[0] ) {
1830                         gameLocal.Error( "idAFEntity_VehicleSimple '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
1831                 }
1832                 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
1833                 if ( wheelJoints[i] == INVALID_JOINT ) {
1834                         gameLocal.Error( "idAFEntity_VehicleSimple '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
1835                 }
1836
1837                 GetAnimator()->GetJointTransform( wheelJoints[i], 0, origin, axis );
1838                 origin = renderEntity.origin + origin * renderEntity.axis;
1839
1840                 suspension[i] = new idAFConstraint_Suspension();
1841                 suspension[i]->Setup( va( "suspension%d", i ), af.GetPhysics()->GetBody( 0 ), origin, af.GetPhysics()->GetAxis( 0 ), wheelModel );
1842                 suspension[i]->SetSuspension(   g_vehicleSuspensionUp.GetFloat(),
1843                                                                                 g_vehicleSuspensionDown.GetFloat(),
1844                                                                                 g_vehicleSuspensionKCompress.GetFloat(),
1845                                                                                 g_vehicleSuspensionDamping.GetFloat(),
1846                                                                                 g_vehicleTireFriction.GetFloat() );
1847
1848                 af.GetPhysics()->AddConstraint( suspension[i] );
1849         }
1850
1851         memset( wheelAngles, 0, sizeof( wheelAngles ) );
1852         BecomeActive( TH_THINK );
1853 }
1854
1855 /*
1856 ================
1857 idAFEntity_VehicleSimple::Think
1858 ================
1859 */
1860 void idAFEntity_VehicleSimple::Think( void ) {
1861         int i;
1862         float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
1863         idVec3 origin;
1864         idMat3 axis;
1865         idRotation wheelRotation, steerRotation;
1866
1867         if ( thinkFlags & TH_THINK ) {
1868
1869                 if ( player ) {
1870                         // capture the input from a player
1871                         velocity = g_vehicleVelocity.GetFloat();
1872                         if ( player->usercmd.forwardmove < 0 ) {
1873                                 velocity = -velocity;
1874                         }
1875                         force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
1876                         steerAngle = GetSteerAngle();
1877                 }
1878
1879                 // update the wheel motor force and steering
1880                 for ( i = 0; i < 2; i++ ) {
1881
1882                         // front wheel drive
1883                         if ( velocity != 0.0f ) {
1884                                 suspension[i]->EnableMotor( true );
1885                         } else {
1886                                 suspension[i]->EnableMotor( false );
1887                         }
1888                         suspension[i]->SetMotorVelocity( velocity );
1889                         suspension[i]->SetMotorForce( force );
1890
1891                         // update the wheel steering
1892                         suspension[i]->SetSteerAngle( steerAngle );
1893                 }
1894
1895                 // adjust wheel velocity for better steering because there are no differentials between the wheels
1896                 if ( steerAngle < 0.0f ) {
1897                         suspension[0]->SetMotorVelocity( velocity * 0.5f );
1898                 } else if ( steerAngle > 0.0f ) {
1899                         suspension[1]->SetMotorVelocity( velocity * 0.5f );
1900                 }
1901
1902                 // update suspension with latest cvar settings
1903                 for ( i = 0; i < 4; i++ ) {
1904                         suspension[i]->SetSuspension(   g_vehicleSuspensionUp.GetFloat(),
1905                                                                                         g_vehicleSuspensionDown.GetFloat(),
1906                                                                                         g_vehicleSuspensionKCompress.GetFloat(),
1907                                                                                         g_vehicleSuspensionDamping.GetFloat(),
1908                                                                                         g_vehicleTireFriction.GetFloat() );
1909                 }
1910
1911                 // run the physics
1912                 RunPhysics();
1913
1914                 // move and rotate the wheels visually
1915                 for ( i = 0; i < 4; i++ ) {
1916                         idAFBody *body = af.GetPhysics()->GetBody( 0 );
1917
1918                         origin = suspension[i]->GetWheelOrigin();
1919                         velocity = body->GetPointVelocity( origin ) * body->GetWorldAxis()[0];
1920                         wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
1921
1922                         // additional rotation about the wheel axis
1923                         wheelRotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
1924                         wheelRotation.SetVec( 0, -1, 0 );
1925
1926                         if ( i < 2 ) {
1927                                 // rotate the wheel for steering
1928                                 steerRotation.SetAngle( steerAngle );
1929                                 steerRotation.SetVec( 0, 0, 1 );
1930                                 // set wheel rotation
1931                                 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() * steerRotation.ToMat3() );
1932                         } else {
1933                                 // set wheel rotation
1934                                 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() );
1935                         }
1936
1937                         // set wheel position for suspension
1938                         origin = ( origin - renderEntity.origin ) * renderEntity.axis.Transpose();
1939                         GetAnimator()->SetJointPos( wheelJoints[i], JOINTMOD_WORLD_OVERRIDE, origin );
1940                 }
1941 /*
1942                 // spawn dust particle effects
1943                 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
1944                         int numContacts;
1945                         idAFConstraint_Contact *contacts[2];
1946                         for ( i = 0; i < 4; i++ ) {
1947                                 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
1948                                 for ( int j = 0; j < numContacts; j++ ) {
1949                                         gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
1950                                 }
1951                         }
1952                 }
1953 */
1954         }
1955
1956         UpdateAnimation();
1957         if ( thinkFlags & TH_UPDATEVISUALS ) {
1958                 Present();
1959                 LinkCombat();
1960         }
1961 }
1962
1963
1964 /*
1965 ===============================================================================
1966
1967   idAFEntity_VehicleFourWheels
1968
1969 ===============================================================================
1970 */
1971
1972 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleFourWheels )
1973 END_CLASS
1974
1975
1976 /*
1977 ================
1978 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels
1979 ================
1980 */
1981 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels( void ) {
1982         int i;
1983
1984         for ( i = 0; i < 4; i++ ) {
1985                 wheels[i]               = NULL;
1986                 wheelJoints[i]  = INVALID_JOINT;
1987                 wheelAngles[i]  = 0.0f;
1988         }
1989         steering[0]                     = NULL;
1990         steering[1]                     = NULL;
1991 }
1992
1993 /*
1994 ================
1995 idAFEntity_VehicleFourWheels::Spawn
1996 ================
1997 */
1998 void idAFEntity_VehicleFourWheels::Spawn( void ) {
1999         int i;
2000         static const char *wheelBodyKeys[] = {
2001                 "wheelBodyFrontLeft",
2002                 "wheelBodyFrontRight",
2003                 "wheelBodyRearLeft",
2004                 "wheelBodyRearRight"
2005         };
2006         static const char *wheelJointKeys[] = {
2007                 "wheelJointFrontLeft",
2008                 "wheelJointFrontRight",
2009                 "wheelJointRearLeft",
2010                 "wheelJointRearRight"
2011         };
2012         static const char *steeringHingeKeys[] = {
2013                 "steeringHingeFrontLeft",
2014                 "steeringHingeFrontRight",
2015         };
2016
2017         const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2018
2019         for ( i = 0; i < 4; i++ ) {
2020                 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2021                 if ( !wheelBodyName[0] ) {
2022                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2023                 }
2024                 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2025                 if ( !wheels[i] ) {
2026                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2027                 }
2028                 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2029                 if ( !wheelJointName[0] ) {
2030                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2031                 }
2032                 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2033                 if ( wheelJoints[i] == INVALID_JOINT ) {
2034                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2035                 }
2036         }
2037
2038         for ( i = 0; i < 2; i++ ) {
2039                 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2040                 if ( !steeringHingeName[0] ) {
2041                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2042                 }
2043                 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2044                 if ( !steering[i] ) {
2045                         gameLocal.Error( "idAFEntity_VehicleFourWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2046                 }
2047         }
2048
2049         memset( wheelAngles, 0, sizeof( wheelAngles ) );
2050         BecomeActive( TH_THINK );
2051 }
2052
2053 /*
2054 ================
2055 idAFEntity_VehicleFourWheels::Think
2056 ================
2057 */
2058 void idAFEntity_VehicleFourWheels::Think( void ) {
2059         int i;
2060         float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2061         idVec3 origin;
2062         idMat3 axis;
2063         idRotation rotation;
2064
2065         if ( thinkFlags & TH_THINK ) {
2066
2067                 if ( player ) {
2068                         // capture the input from a player
2069                         velocity = g_vehicleVelocity.GetFloat();
2070                         if ( player->usercmd.forwardmove < 0 ) {
2071                                 velocity = -velocity;
2072                         }
2073                         force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2074                         steerAngle = GetSteerAngle();
2075                 }
2076
2077                 // update the wheel motor force
2078                 for ( i = 0; i < 2; i++ ) {
2079                         wheels[2+i]->SetContactMotorVelocity( velocity );
2080                         wheels[2+i]->SetContactMotorForce( force );
2081                 }
2082
2083                 // adjust wheel velocity for better steering because there are no differentials between the wheels
2084                 if ( steerAngle < 0.0f ) {
2085                         wheels[2]->SetContactMotorVelocity( velocity * 0.5f );
2086                 }
2087                 else if ( steerAngle > 0.0f ) {
2088                         wheels[3]->SetContactMotorVelocity( velocity * 0.5f );
2089                 }
2090
2091                 // update the wheel steering
2092                 steering[0]->SetSteerAngle( steerAngle );
2093                 steering[1]->SetSteerAngle( steerAngle );
2094                 for ( i = 0; i < 2; i++ ) {
2095                         steering[i]->SetSteerSpeed( 3.0f );
2096                 }
2097
2098                 // update the steering wheel
2099                 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2100                 rotation.SetVec( axis[2] );
2101                 rotation.SetAngle( -steerAngle );
2102                 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2103
2104                 // run the physics
2105                 RunPhysics();
2106
2107                 // rotate the wheels visually
2108                 for ( i = 0; i < 4; i++ ) {
2109                         if ( force == 0.0f ) {
2110                                 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2111                         }
2112                         wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2113                         // give the wheel joint an additional rotation about the wheel axis
2114                         rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2115                         axis = af.GetPhysics()->GetAxis( 0 );
2116                         rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2117                         animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2118                 }
2119
2120                 // spawn dust particle effects
2121                 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2122                         int numContacts;
2123                         idAFConstraint_Contact *contacts[2];
2124                         for ( i = 0; i < 4; i++ ) {
2125                                 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2126                                 for ( int j = 0; j < numContacts; j++ ) {
2127                                         gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
2128                                 }
2129                         }
2130                 }
2131         }
2132
2133         UpdateAnimation();
2134         if ( thinkFlags & TH_UPDATEVISUALS ) {
2135                 Present();
2136                 LinkCombat();
2137         }
2138 }
2139
2140
2141 /*
2142 ===============================================================================
2143
2144   idAFEntity_VehicleSixWheels
2145
2146 ===============================================================================
2147 */
2148
2149 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSixWheels )
2150 END_CLASS
2151
2152         /*
2153 ================
2154 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels
2155 ================
2156 */
2157 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels( void ) {
2158         int i;
2159
2160         for ( i = 0; i < 6; i++ ) {
2161                 wheels[i]               = NULL;
2162                 wheelJoints[i]  = INVALID_JOINT;
2163                 wheelAngles[i]  = 0.0f;
2164         }
2165         steering[0]                     = NULL;
2166         steering[1]                     = NULL;
2167         steering[2]                     = NULL;
2168         steering[3]                     = NULL;
2169 }
2170
2171 /*
2172 ================
2173 idAFEntity_VehicleSixWheels::Spawn
2174 ================
2175 */
2176 void idAFEntity_VehicleSixWheels::Spawn( void ) {
2177         int i;
2178         static const char *wheelBodyKeys[] = {
2179                 "wheelBodyFrontLeft",
2180                 "wheelBodyFrontRight",
2181                 "wheelBodyMiddleLeft",
2182                 "wheelBodyMiddleRight",
2183                 "wheelBodyRearLeft",
2184                 "wheelBodyRearRight"
2185         };
2186         static const char *wheelJointKeys[] = {
2187                 "wheelJointFrontLeft",
2188                 "wheelJointFrontRight",
2189                 "wheelJointMiddleLeft",
2190                 "wheelJointMiddleRight",
2191                 "wheelJointRearLeft",
2192                 "wheelJointRearRight"
2193         };
2194         static const char *steeringHingeKeys[] = {
2195                 "steeringHingeFrontLeft",
2196                 "steeringHingeFrontRight",
2197                 "steeringHingeRearLeft",
2198                 "steeringHingeRearRight"
2199         };
2200
2201         const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2202
2203         for ( i = 0; i < 6; i++ ) {
2204                 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2205                 if ( !wheelBodyName[0] ) {
2206                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2207                 }
2208                 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2209                 if ( !wheels[i] ) {
2210                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2211                 }
2212                 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2213                 if ( !wheelJointName[0] ) {
2214                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2215                 }
2216                 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2217                 if ( wheelJoints[i] == INVALID_JOINT ) {
2218                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2219                 }
2220         }
2221
2222         for ( i = 0; i < 4; i++ ) {
2223                 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2224                 if ( !steeringHingeName[0] ) {
2225                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2226                 }
2227                 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2228                 if ( !steering[i] ) {
2229                         gameLocal.Error( "idAFEntity_VehicleSixWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2230                 }
2231         }
2232
2233         memset( wheelAngles, 0, sizeof( wheelAngles ) );
2234         BecomeActive( TH_THINK );
2235 }
2236
2237 /*
2238 ================
2239 idAFEntity_VehicleSixWheels::Think
2240 ================
2241 */
2242 void idAFEntity_VehicleSixWheels::Think( void ) {
2243         int i;
2244 #ifndef _D3XP
2245         float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2246 #endif
2247         idVec3 origin;
2248         idMat3 axis;
2249         idRotation rotation;
2250
2251         if ( thinkFlags & TH_THINK ) {
2252
2253                 if ( player ) {
2254                         // capture the input from a player
2255                         velocity = g_vehicleVelocity.GetFloat();
2256                         if ( player->usercmd.forwardmove < 0 ) {
2257                                 velocity = -velocity;
2258                         }
2259                         force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2260                         steerAngle = GetSteerAngle();
2261                 }
2262
2263                 // update the wheel motor force
2264                 for ( i = 0; i < 6; i++ ) {
2265                         wheels[i]->SetContactMotorVelocity( velocity );
2266                         wheels[i]->SetContactMotorForce( force );
2267                 }
2268
2269                 // adjust wheel velocity for better steering because there are no differentials between the wheels
2270                 if ( steerAngle < 0.0f ) {
2271                         for ( i = 0; i < 3; i++ ) {
2272                                 wheels[(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2273                         }
2274                 }
2275                 else if ( steerAngle > 0.0f ) {
2276                         for ( i = 0; i < 3; i++ ) {
2277                                 wheels[1+(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2278                         }
2279                 }
2280
2281                 // update the wheel steering
2282                 steering[0]->SetSteerAngle( steerAngle );
2283                 steering[1]->SetSteerAngle( steerAngle );
2284                 steering[2]->SetSteerAngle( -steerAngle );
2285                 steering[3]->SetSteerAngle( -steerAngle );
2286                 for ( i = 0; i < 4; i++ ) {
2287                         steering[i]->SetSteerSpeed( 3.0f );
2288                 }
2289
2290                 // update the steering wheel
2291                 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2292                 rotation.SetVec( axis[2] );
2293                 rotation.SetAngle( -steerAngle );
2294                 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2295
2296                 // run the physics
2297                 RunPhysics();
2298
2299                 // rotate the wheels visually
2300                 for ( i = 0; i < 6; i++ ) {
2301                         if ( force == 0.0f ) {
2302                                 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2303                         }
2304                         wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2305                         // give the wheel joint an additional rotation about the wheel axis
2306                         rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2307                         axis = af.GetPhysics()->GetAxis( 0 );
2308                         rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2309                         animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2310                 }
2311
2312                 // spawn dust particle effects
2313                 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2314                         int numContacts;
2315                         idAFConstraint_Contact *contacts[2];
2316                         for ( i = 0; i < 6; i++ ) {
2317                                 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2318                                 for ( int j = 0; j < numContacts; j++ ) {
2319                                         gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
2320                                 }
2321                         }
2322                 }
2323         }
2324
2325         UpdateAnimation();
2326         if ( thinkFlags & TH_UPDATEVISUALS ) {
2327                 Present();
2328                 LinkCombat();
2329         }
2330 }
2331
2332 #ifdef _D3XP
2333 /*
2334 ===============================================================================
2335
2336 idAFEntity_VehicleAutomated
2337
2338 ===============================================================================
2339 */
2340 const idEventDef EV_Vehicle_setVelocity( "setVelocity", "f" );
2341 const idEventDef EV_Vehicle_setTorque( "setTorque", "f" );
2342 const idEventDef EV_Vehicle_setSteeringSpeed( "setSteeringSpeed", "f" );
2343 const idEventDef EV_Vehicle_setWaypoint( "setWaypoint", "e" );
2344
2345 CLASS_DECLARATION( idAFEntity_VehicleSixWheels, idAFEntity_VehicleAutomated )
2346 EVENT( EV_PostSpawn,                            idAFEntity_VehicleAutomated::PostSpawn )
2347 EVENT( EV_Vehicle_setVelocity,          idAFEntity_VehicleAutomated::Event_SetVelocity )
2348 EVENT( EV_Vehicle_setTorque,            idAFEntity_VehicleAutomated::Event_SetTorque )
2349 EVENT( EV_Vehicle_setSteeringSpeed,     idAFEntity_VehicleAutomated::Event_SetSteeringSpeed )
2350 EVENT( EV_Vehicle_setWaypoint,          idAFEntity_VehicleAutomated::Event_SetWayPoint )
2351 END_CLASS
2352
2353 /*
2354 ================
2355 idAFEntity_VehicleAutomated::Spawn
2356 ================
2357 */
2358 void idAFEntity_VehicleAutomated::Spawn( void ) {
2359
2360         velocity = force = steerAngle = 0.f;
2361         currentSteering = steeringSpeed = 0.f;
2362         originHeight = 0.f;
2363         waypoint = NULL;
2364
2365         spawnArgs.GetFloat( "velocity", "150", velocity );
2366         spawnArgs.GetFloat( "torque", "200000", force );
2367         spawnArgs.GetFloat( "steeringSpeed", "1", steeringSpeed );
2368         spawnArgs.GetFloat( "originHeight", "0", originHeight );
2369
2370         PostEventMS( &EV_PostSpawn, 0 );
2371 }
2372
2373 /*
2374 ================
2375 idAFEntity_VehicleAutomated::PostSpawn
2376 ================
2377 */
2378 void idAFEntity_VehicleAutomated::PostSpawn( void ) {
2379
2380         if ( targets.Num() ) {
2381                 waypoint = targets[0].GetEntity();
2382         }
2383 }
2384
2385 /*
2386 ================
2387 idAFEntity_VehicleAutomated::Event_SetVelocity
2388 ================
2389 */
2390 void idAFEntity_VehicleAutomated::Event_SetVelocity( float _velocity ) {
2391         velocity = _velocity;
2392 }
2393
2394 /*
2395 ================
2396 idAFEntity_VehicleAutomated::Event_SetTorque
2397 ================
2398 */
2399 void idAFEntity_VehicleAutomated::Event_SetTorque( float _torque ) {
2400         force = _torque;
2401 }
2402
2403 /*
2404 ================
2405 idAFEntity_VehicleAutomated::Event_SetSteeringSpeed
2406 ================
2407 */
2408 void idAFEntity_VehicleAutomated::Event_SetSteeringSpeed( float _steeringSpeed ) {
2409         steeringSpeed = _steeringSpeed;
2410 }
2411
2412 /*
2413 ================
2414 idAFEntity_VehicleAutomated::Event_SetWayPoint
2415 ================
2416 */
2417 void idAFEntity_VehicleAutomated::Event_SetWayPoint( idEntity *_waypoint ) {
2418         waypoint = _waypoint;
2419 }
2420
2421 /*
2422 ================
2423 idAFEntity_VehicleAutomated::Think
2424 ================
2425 */
2426 #define HIT_WAYPOINT_THRESHOLD  80.f
2427
2428 void idAFEntity_VehicleAutomated::Think( void ) {
2429
2430         // If we don't have a waypoint, coast to a stop
2431         if ( !waypoint ) {
2432                 velocity = force = steerAngle = 0.f;
2433                 idAFEntity_VehicleSixWheels::Think();
2434                 return;
2435         }
2436
2437         idVec3 waypoint_origin, vehicle_origin;
2438         idVec3 travel_vector;
2439         float distance_from_waypoint;
2440
2441         // Set up the vector from the vehicle origin, to the waypoint
2442         vehicle_origin = GetPhysics()->GetOrigin();
2443         vehicle_origin.z -= originHeight;
2444
2445         waypoint_origin = waypoint->GetPhysics()->GetOrigin();
2446
2447         travel_vector = waypoint_origin - vehicle_origin;
2448         distance_from_waypoint = travel_vector.Length();
2449
2450         // Check if we've hit the waypoint (within a certain threshold)
2451         if ( distance_from_waypoint < HIT_WAYPOINT_THRESHOLD ) {
2452                 idStr                           callfunc;
2453                 const function_t        *func;
2454                 idThread                        *thread;
2455
2456                 // Waypoints can call script functions
2457                 waypoint->spawnArgs.GetString( "call", "", callfunc );
2458                 if ( callfunc.Length() ) {
2459                         func = gameLocal.program.FindFunction( callfunc );
2460                         if ( func != NULL ) {
2461                                 thread = new idThread( func );
2462                                 thread->DelayedStart( 0 );
2463                         }
2464                 }
2465
2466                 // Get next waypoint
2467                 if ( waypoint->targets.Num() ) {
2468                         waypoint = waypoint->targets[0].GetEntity();
2469                 } else {
2470                         waypoint = NULL;
2471                 }
2472
2473                 // We are switching waypoints, adjust steering next frame
2474                 idAFEntity_VehicleSixWheels::Think();
2475                 return;
2476         }
2477
2478         idAngles vehicle_angles, travel_angles;
2479
2480         // Get the angles we need to steer towards
2481         travel_angles = travel_vector.ToAngles().Normalize360();
2482         vehicle_angles = this->GetPhysics()->GetAxis().ToAngles().Normalize360();
2483
2484         float   delta_yaw;
2485
2486         // Get the shortest steering angle towards the travel angles
2487         delta_yaw = vehicle_angles.yaw - travel_angles.yaw;
2488         if ( idMath::Fabs( delta_yaw ) > 180.f ) {
2489                 if ( delta_yaw > 0 ) {
2490                         delta_yaw = delta_yaw - 360;
2491                 } else {
2492                         delta_yaw = delta_yaw + 360;
2493                 }
2494         }
2495
2496         // Maximum steering angle is 35 degrees
2497         delta_yaw = idMath::ClampFloat( -35.f, 35.f, delta_yaw );
2498
2499         idealSteering = delta_yaw;
2500
2501         // Adjust steering incrementally so it doesn't snap to the ideal angle
2502         if ( idMath::Fabs( (idealSteering - currentSteering) ) > steeringSpeed ) {
2503                 if ( idealSteering > currentSteering ) {
2504                         currentSteering += steeringSpeed;
2505                 } else {
2506                         currentSteering -= steeringSpeed;
2507                 }
2508         } else {
2509                 currentSteering = idealSteering;
2510         }
2511
2512         // DEBUG
2513         if ( g_vehicleDebug.GetBool() ) {
2514                 gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), vehicle_origin );
2515                 gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), waypoint_origin );
2516                 gameRenderWorld->DrawText( waypoint->name.c_str(), waypoint_origin + idVec3(0,0,16), 0.25f, colorYellow, gameLocal.GetLocalPlayer()->viewAxis );
2517                 gameRenderWorld->DebugArrow( colorWhite, vehicle_origin, waypoint_origin, 12.f );
2518         }
2519
2520         // Set the final steerAngle for the vehicle
2521         steerAngle = currentSteering;
2522
2523         idAFEntity_VehicleSixWheels::Think();
2524 }
2525 #endif
2526
2527 /*
2528 ===============================================================================
2529
2530   idAFEntity_SteamPipe
2531
2532 ===============================================================================
2533 */
2534
2535 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_SteamPipe )
2536 END_CLASS
2537
2538
2539 /*
2540 ================
2541 idAFEntity_SteamPipe::idAFEntity_SteamPipe
2542 ================
2543 */
2544 idAFEntity_SteamPipe::idAFEntity_SteamPipe( void ) {
2545         steamBody                       = 0;
2546         steamForce                      = 0.0f;
2547         steamUpForce            = 0.0f;
2548         steamModelDefHandle     = -1;
2549         memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2550 }
2551
2552 /*
2553 ================
2554 idAFEntity_SteamPipe::~idAFEntity_SteamPipe
2555 ================
2556 */
2557 idAFEntity_SteamPipe::~idAFEntity_SteamPipe( void ) {
2558         if ( steamModelDefHandle >= 0 ){
2559                 gameRenderWorld->FreeEntityDef( steamModelDefHandle );
2560         }
2561 }
2562
2563 /*
2564 ================
2565 idAFEntity_SteamPipe::Save
2566 ================
2567 */
2568 void idAFEntity_SteamPipe::Save( idSaveGame *savefile ) const {
2569 }
2570
2571 /*
2572 ================
2573 idAFEntity_SteamPipe::Restore
2574 ================
2575 */
2576 void idAFEntity_SteamPipe::Restore( idRestoreGame *savefile ) {
2577         Spawn();
2578 }
2579
2580 /*
2581 ================
2582 idAFEntity_SteamPipe::Spawn
2583 ================
2584 */
2585 void idAFEntity_SteamPipe::Spawn( void ) {
2586         idVec3 steamDir;
2587         const char *steamBodyName;
2588
2589         LoadAF();
2590
2591         SetCombatModel();
2592
2593         SetPhysics( af.GetPhysics() );
2594
2595         fl.takedamage = true;
2596
2597         steamBodyName = spawnArgs.GetString( "steamBody", "" );
2598         steamForce = spawnArgs.GetFloat( "steamForce", "2000" );
2599         steamUpForce = spawnArgs.GetFloat( "steamUpForce", "10" );
2600         steamDir = af.GetPhysics()->GetAxis( steamBody )[2];
2601         steamBody = af.GetPhysics()->GetBodyId( steamBodyName );
2602         force.SetPosition( af.GetPhysics(), steamBody, af.GetPhysics()->GetOrigin( steamBody ) );
2603         force.SetForce( steamDir * -steamForce );
2604
2605         InitSteamRenderEntity();
2606
2607         BecomeActive( TH_THINK );
2608 }
2609
2610 /*
2611 ================
2612 idAFEntity_SteamPipe::InitSteamRenderEntity
2613 ================
2614 */
2615 void idAFEntity_SteamPipe::InitSteamRenderEntity( void ) {
2616         const char      *temp;
2617         const idDeclModelDef *modelDef;
2618
2619         memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2620         steamRenderEntity.shaderParms[ SHADERPARM_RED ]         = 1.0f;
2621         steamRenderEntity.shaderParms[ SHADERPARM_GREEN ]       = 1.0f;
2622         steamRenderEntity.shaderParms[ SHADERPARM_BLUE ]        = 1.0f;
2623         modelDef = NULL;
2624         temp = spawnArgs.GetString ( "model_steam" );
2625         if ( *temp != '\0' ) {
2626                 if ( !strstr( temp, "." ) ) {
2627                         modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, temp, false ) );
2628                         if ( modelDef ) {
2629                                 steamRenderEntity.hModel = modelDef->ModelHandle();
2630                         }
2631                 }
2632
2633                 if ( !steamRenderEntity.hModel ) {
2634                         steamRenderEntity.hModel = renderModelManager->FindModel( temp );
2635                 }
2636
2637                 if ( steamRenderEntity.hModel ) {
2638                         steamRenderEntity.bounds = steamRenderEntity.hModel->Bounds( &steamRenderEntity );
2639                 } else {
2640                         steamRenderEntity.bounds.Zero();
2641                 }
2642                 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2643                 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2644                 steamModelDefHandle = gameRenderWorld->AddEntityDef( &steamRenderEntity );
2645         }
2646 }
2647
2648 /*
2649 ================
2650 idAFEntity_SteamPipe::Think
2651 ================
2652 */
2653 void idAFEntity_SteamPipe::Think( void ) {
2654         idVec3 steamDir;
2655
2656         if ( thinkFlags & TH_THINK ) {
2657                 steamDir.x = gameLocal.random.CRandomFloat() * steamForce;
2658                 steamDir.y = gameLocal.random.CRandomFloat() * steamForce;
2659                 steamDir.z = steamUpForce;
2660                 force.SetForce( steamDir );
2661                 force.Evaluate( gameLocal.time );
2662                 //gameRenderWorld->DebugArrow( colorWhite, af.GetPhysics()->GetOrigin( steamBody ), af.GetPhysics()->GetOrigin( steamBody ) - 10.0f * steamDir, 4 );
2663         }
2664
2665         if ( steamModelDefHandle >= 0 ){
2666                 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2667                 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2668                 gameRenderWorld->UpdateEntityDef( steamModelDefHandle, &steamRenderEntity );
2669         }
2670
2671         idAFEntity_Base::Think();
2672 }
2673
2674
2675 /*
2676 ===============================================================================
2677
2678   idAFEntity_ClawFourFingers
2679
2680 ===============================================================================
2681 */
2682
2683 const idEventDef EV_SetFingerAngle( "setFingerAngle", "f" );
2684 const idEventDef EV_StopFingers( "stopFingers" );
2685
2686 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_ClawFourFingers )
2687         EVENT( EV_SetFingerAngle,               idAFEntity_ClawFourFingers::Event_SetFingerAngle )
2688         EVENT( EV_StopFingers,                  idAFEntity_ClawFourFingers::Event_StopFingers )
2689 END_CLASS
2690
2691 static const char *clawConstraintNames[] = {
2692         "claw1", "claw2", "claw3", "claw4"
2693 };
2694
2695 /*
2696 ================
2697 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers
2698 ================
2699 */
2700 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers( void ) {
2701         fingers[0]      = NULL;
2702         fingers[1]      = NULL;
2703         fingers[2]      = NULL;
2704         fingers[3]      = NULL;
2705 }
2706
2707 /*
2708 ================
2709 idAFEntity_ClawFourFingers::Save
2710 ================
2711 */
2712 void idAFEntity_ClawFourFingers::Save( idSaveGame *savefile ) const {
2713         int i;
2714
2715         for ( i = 0; i < 4; i++ ) {
2716                 fingers[i]->Save( savefile );
2717         }
2718 }
2719
2720 /*
2721 ================
2722 idAFEntity_ClawFourFingers::Restore
2723 ================
2724 */
2725 void idAFEntity_ClawFourFingers::Restore( idRestoreGame *savefile ) {
2726         int i;
2727
2728         for ( i = 0; i < 4; i++ ) {
2729                 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2730                 fingers[i]->Restore( savefile );
2731         }
2732
2733         SetCombatModel();
2734         LinkCombat();
2735 }
2736
2737 /*
2738 ================
2739 idAFEntity_ClawFourFingers::Spawn
2740 ================
2741 */
2742 void idAFEntity_ClawFourFingers::Spawn( void ) {
2743         int i;
2744
2745         LoadAF();
2746
2747         SetCombatModel();
2748
2749         af.GetPhysics()->LockWorldConstraints( true );
2750         af.GetPhysics()->SetForcePushable( true );
2751         SetPhysics( af.GetPhysics() );
2752
2753         fl.takedamage = true;
2754
2755         for ( i = 0; i < 4; i++ ) {
2756                 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2757                 if ( !fingers[i] ) {
2758                         gameLocal.Error( "idClaw_FourFingers '%s': can't find claw constraint '%s'", name.c_str(), clawConstraintNames[i] );
2759                 }
2760         }
2761 }
2762
2763 /*
2764 ================
2765 idAFEntity_ClawFourFingers::Event_SetFingerAngle
2766 ================
2767 */
2768 void idAFEntity_ClawFourFingers::Event_SetFingerAngle( float angle ) {
2769         int i;
2770
2771         for ( i = 0; i < 4; i++ ) {
2772                 fingers[i]->SetSteerAngle( angle );
2773                 fingers[i]->SetSteerSpeed( 0.5f );
2774         }
2775         af.GetPhysics()->Activate();
2776 }
2777
2778 /*
2779 ================
2780 idAFEntity_ClawFourFingers::Event_StopFingers
2781 ================
2782 */
2783 void idAFEntity_ClawFourFingers::Event_StopFingers( void ) {
2784         int i;
2785
2786         for ( i = 0; i < 4; i++ ) {
2787                 fingers[i]->SetSteerAngle( fingers[i]->GetAngle() );
2788         }
2789 }
2790
2791
2792 /*
2793 ===============================================================================
2794
2795   editor support routines
2796
2797 ===============================================================================
2798 */
2799
2800
2801 /*
2802 ================
2803 idGameEdit::AF_SpawnEntity
2804 ================
2805 */
2806 bool idGameEdit::AF_SpawnEntity( const char *fileName ) {
2807         idDict args;
2808         idPlayer *player;
2809         idAFEntity_Generic *ent;
2810         const idDeclAF *af;
2811         idVec3 org;
2812         float yaw;
2813
2814         player = gameLocal.GetLocalPlayer();
2815         if ( !player || !gameLocal.CheatsOk( false ) ) {
2816                 return false;
2817         }
2818
2819         af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, fileName ) );
2820         if ( !af ) {
2821                 return false;
2822         }
2823
2824         yaw = player->viewAngles.yaw;
2825         args.Set( "angle", va( "%f", yaw + 180 ) );
2826         org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
2827         args.Set( "origin", org.ToString() );
2828         args.Set( "spawnclass", "idAFEntity_Generic" );
2829         if ( af->model[0] ) {
2830                 args.Set( "model", af->model.c_str() );
2831         } else {
2832                 args.Set( "model", fileName );
2833         }
2834         if ( af->skin[0] ) {
2835                 args.Set( "skin", af->skin.c_str() );
2836         }
2837         args.Set( "articulatedFigure", fileName );
2838         args.Set( "nodrop", "1" );
2839         ent = static_cast<idAFEntity_Generic *>(gameLocal.SpawnEntityType( idAFEntity_Generic::Type, &args));
2840
2841         // always update this entity
2842         ent->BecomeActive( TH_THINK );
2843         ent->KeepRunningPhysics();
2844         ent->fl.forcePhysicsUpdate = true;
2845
2846         player->dragEntity.SetSelected( ent );
2847
2848         return true;
2849 }
2850
2851 /*
2852 ================
2853 idGameEdit::AF_UpdateEntities
2854 ================
2855 */
2856 void idGameEdit::AF_UpdateEntities( const char *fileName ) {
2857         idEntity *ent;
2858         idAFEntity_Base *af;
2859         idStr name;
2860
2861         name = fileName;
2862         name.StripFileExtension();
2863
2864         // reload any idAFEntity_Generic which uses the given articulated figure file
2865         for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2866                 if ( ent->IsType( idAFEntity_Base::Type ) ) {
2867                         af = static_cast<idAFEntity_Base *>(ent);
2868                         if ( name.Icmp( af->GetAFName() ) == 0 ) {
2869                                 af->LoadAF();
2870                                 af->GetAFPhysics()->PutToRest();
2871                         }
2872                 }
2873         }
2874 }
2875
2876 /*
2877 ================
2878 idGameEdit::AF_UndoChanges
2879 ================
2880 */
2881 void idGameEdit::AF_UndoChanges( void ) {
2882         int i, c;
2883         idEntity *ent;
2884         idAFEntity_Base *af;
2885         idDeclAF *decl;
2886
2887         c = declManager->GetNumDecls( DECL_AF );
2888         for ( i = 0; i < c; i++ ) {
2889                 decl = static_cast<idDeclAF *>( const_cast<idDecl *>( declManager->DeclByIndex( DECL_AF, i, false ) ) );
2890                 if ( !decl->modified ) {
2891                         continue;
2892                 }
2893
2894                 decl->Invalidate();
2895                 declManager->FindType( DECL_AF, decl->GetName() );
2896
2897                 // reload all AF entities using the file
2898                 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2899                         if ( ent->IsType( idAFEntity_Base::Type ) ) {
2900                                 af = static_cast<idAFEntity_Base *>(ent);
2901                                 if ( idStr::Icmp( decl->GetName(), af->GetAFName() ) == 0 ) {
2902                                         af->LoadAF();
2903                                 }
2904                         }
2905                 }
2906         }
2907 }
2908
2909 /*
2910 ================
2911 GetJointTransform
2912 ================
2913 */
2914 typedef struct {
2915         renderEntity_t *ent;
2916         const idMD5Joint *joints;
2917 } jointTransformData_t;
2918
2919 static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
2920         int i;
2921         jointTransformData_t *data = reinterpret_cast<jointTransformData_t *>(model);
2922
2923         for ( i = 0; i < data->ent->numJoints; i++ ) {
2924                 if ( data->joints[i].name.Icmp( jointName ) == 0 ) {
2925                         break;
2926                 }
2927         }
2928         if ( i >= data->ent->numJoints ) {
2929                 return false;
2930         }
2931         origin = frame[i].ToVec3();
2932         axis = frame[i].ToMat3();
2933         return true;
2934 }
2935
2936 /*
2937 ================
2938 GetArgString
2939 ================
2940 */
2941 static const char *GetArgString( const idDict &args, const idDict *defArgs, const char *key ) {
2942         const char *s;
2943
2944         s = args.GetString( key );
2945         if ( !s[0] && defArgs ) {
2946                 s = defArgs->GetString( key );
2947         }
2948         return s;
2949 }
2950
2951 /*
2952 ================
2953 idGameEdit::AF_CreateMesh
2954 ================
2955 */
2956 idRenderModel *idGameEdit::AF_CreateMesh( const idDict &args, idVec3 &meshOrigin, idMat3 &meshAxis, bool &poseIsSet ) {
2957         int i, jointNum;
2958         const idDeclAF *af;
2959         const idDeclAF_Body *fb;
2960         renderEntity_t ent;
2961         idVec3 origin, *bodyOrigin, *newBodyOrigin, *modifiedOrigin;
2962         idMat3 axis, *bodyAxis, *newBodyAxis, *modifiedAxis;
2963         declAFJointMod_t *jointMod;
2964         idAngles angles;
2965         const idDict *defArgs;
2966         const idKeyValue *arg;
2967         idStr name;
2968         jointTransformData_t data;
2969         const char *classname, *afName, *modelName;
2970         idRenderModel *md5;
2971         const idDeclModelDef *modelDef;
2972         const idMD5Anim *MD5anim;
2973         const idMD5Joint *MD5joint;
2974         const idMD5Joint *MD5joints;
2975         int numMD5joints;
2976         idJointMat *originalJoints;
2977         int parentNum;
2978
2979         poseIsSet = false;
2980         meshOrigin.Zero();
2981         meshAxis.Identity();
2982
2983         classname = args.GetString( "classname" );
2984         defArgs = gameLocal.FindEntityDefDict( classname );
2985
2986         // get the articulated figure
2987         afName = GetArgString( args, defArgs, "articulatedFigure" );
2988         af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, afName ) );
2989         if ( !af ) {
2990                 return NULL;
2991         }
2992
2993         // get the md5 model
2994         modelName = GetArgString( args, defArgs, "model" );
2995         modelDef = static_cast< const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
2996         if ( !modelDef ) {
2997                 return NULL;
2998         }
2999
3000         // make sure model hasn't been purged
3001         if ( modelDef->ModelHandle() && !modelDef->ModelHandle()->IsLoaded() ) {
3002                 modelDef->ModelHandle()->LoadModel();
3003         }
3004
3005         // get the md5
3006         md5 = modelDef->ModelHandle();
3007         if ( !md5 || md5->IsDefaultModel() ) {
3008                 return NULL;
3009         }
3010
3011         // get the articulated figure pose anim
3012         int animNum = modelDef->GetAnim( "af_pose" );
3013         if ( !animNum ) {
3014                 return NULL;
3015         }
3016         const idAnim *anim = modelDef->GetAnim( animNum );
3017         if ( !anim ) {
3018                 return NULL;
3019         }
3020         MD5anim = anim->MD5Anim( 0 );
3021         MD5joints = md5->GetJoints();
3022         numMD5joints = md5->NumJoints();
3023
3024         // setup a render entity
3025         memset( &ent, 0, sizeof( ent ) );
3026         ent.customSkin = modelDef->GetSkin();
3027         ent.bounds.Clear();
3028         ent.numJoints = numMD5joints;
3029         ent.joints = ( idJointMat * )_alloca16( ent.numJoints * sizeof( *ent.joints ) );
3030
3031         // create animation from of the af_pose
3032         ANIM_CreateAnimFrame( md5, MD5anim, ent.numJoints, ent.joints, 1, modelDef->GetVisualOffset(), false );
3033
3034         // buffers to store the initial origin and axis for each body
3035         bodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
3036         bodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
3037         newBodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
3038         newBodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
3039
3040         // finish the AF positions
3041         data.ent = &ent;
3042         data.joints = MD5joints;
3043         af->Finish( GetJointTransform, ent.joints, &data );
3044
3045         // get the initial origin and axis for each AF body
3046         for ( i = 0; i < af->bodies.Num(); i++ ) {
3047                 fb = af->bodies[i];
3048
3049                 if ( fb->modelType == TRM_BONE ) {
3050                         // axis of bone trace model
3051                         axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
3052                         axis[2].Normalize();
3053                         axis[2].NormalVectors( axis[0], axis[1] );
3054                         axis[1] = -axis[1];
3055                 } else {
3056                         axis = fb->angles.ToMat3();
3057                 }
3058
3059                 newBodyOrigin[i] = bodyOrigin[i] = fb->origin.ToVec3();
3060                 newBodyAxis[i] = bodyAxis[i] = axis;
3061         }
3062
3063         // get any new body transforms stored in the key/value pairs
3064         for ( arg = args.MatchPrefix( "body ", NULL ); arg; arg = args.MatchPrefix( "body ", arg ) ) {
3065                 name = arg->GetKey();
3066                 name.Strip( "body " );
3067                 for ( i = 0; i < af->bodies.Num(); i++ ) {
3068                         fb = af->bodies[i];
3069                         if ( fb->name.Icmp( name ) == 0 ) {
3070                                 break;
3071                         }
3072                 }
3073                 if ( i >= af->bodies.Num() ) {
3074                         continue;
3075                 }
3076                 sscanf( arg->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
3077
3078                 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
3079                         meshAxis = bodyAxis[i].Transpose() * angles.ToMat3();
3080                         meshOrigin = origin - bodyOrigin[i] * meshAxis;
3081                         poseIsSet = true;
3082                 } else {
3083                         newBodyOrigin[i] = origin;
3084                         newBodyAxis[i] = angles.ToMat3();
3085                 }
3086         }
3087
3088         // save the original joints
3089         originalJoints = ( idJointMat * )_alloca16( numMD5joints * sizeof( originalJoints[0] ) );
3090         memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) );
3091
3092         // buffer to store the joint mods
3093         jointMod = (declAFJointMod_t *) _alloca16( numMD5joints * sizeof( declAFJointMod_t ) );
3094         memset( jointMod, -1, numMD5joints * sizeof( declAFJointMod_t ) );
3095         modifiedOrigin = (idVec3 *) _alloca16( numMD5joints * sizeof( idVec3 ) );
3096         memset( modifiedOrigin, 0, numMD5joints * sizeof( idVec3 ) );
3097         modifiedAxis = (idMat3 *) _alloca16( numMD5joints * sizeof( idMat3 ) );
3098         memset( modifiedAxis, 0, numMD5joints * sizeof( idMat3 ) );
3099
3100         // get all the joint modifications
3101         for ( i = 0; i < af->bodies.Num(); i++ ) {
3102                 fb = af->bodies[i];
3103
3104                 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
3105                         continue;
3106                 }
3107
3108                 for ( jointNum = 0; jointNum < numMD5joints; jointNum++ ) {
3109                         if ( MD5joints[jointNum].name.Icmp( fb->jointName ) == 0 ) {
3110                                 break;
3111                         }
3112                 }
3113
3114                 if ( jointNum >= 0 && jointNum < ent.numJoints ) {
3115                         jointMod[ jointNum ] = fb->jointMod;
3116                         modifiedAxis[ jointNum ] = ( bodyAxis[i] * originalJoints[jointNum].ToMat3().Transpose() ).Transpose() * ( newBodyAxis[i] * meshAxis.Transpose() );
3117                         // FIXME: calculate correct modifiedOrigin
3118                         modifiedOrigin[ jointNum ] = originalJoints[ jointNum ].ToVec3();
3119                 }
3120         }
3121
3122         // apply joint modifications to the skeleton
3123         MD5joint = MD5joints + 1;
3124         for( i = 1; i < numMD5joints; i++, MD5joint++ ) {
3125
3126                 parentNum = MD5joint->parent - MD5joints;
3127                 idMat3 parentAxis = originalJoints[ parentNum ].ToMat3();
3128                 idMat3 localm = originalJoints[i].ToMat3() * parentAxis.Transpose();
3129                 idVec3 localt = ( originalJoints[i].ToVec3() - originalJoints[ parentNum ].ToVec3() ) * parentAxis.Transpose();
3130
3131                 switch( jointMod[i] ) {
3132                         case DECLAF_JOINTMOD_ORIGIN: {
3133                                 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
3134                                 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
3135                                 break;
3136                         }
3137                         case DECLAF_JOINTMOD_AXIS: {
3138                                 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
3139                                 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
3140                                 break;
3141                         }
3142                         case DECLAF_JOINTMOD_BOTH: {
3143                                 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
3144                                 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
3145                                 break;
3146                         }
3147                         default: {
3148                                 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
3149                                 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
3150                                 break;
3151                         }
3152                 }
3153         }
3154
3155         // instantiate a mesh using the joint information from the render entity
3156         return md5->InstantiateDynamicModel( &ent, NULL, NULL );
3157 }
3158
3159 #ifdef _D3XP
3160
3161 /*
3162 ===============================================================================
3163 idHarvestable
3164 ===============================================================================
3165 */
3166
3167 const idEventDef EV_Harvest_SpawnHarvestTrigger( "<spawnHarvestTrigger>", NULL );
3168
3169 CLASS_DECLARATION( idEntity, idHarvestable )
3170 EVENT( EV_Harvest_SpawnHarvestTrigger,  idHarvestable::Event_SpawnHarvestTrigger )
3171 EVENT( EV_Touch,                                                idHarvestable::Event_Touch )
3172 END_CLASS
3173
3174 idHarvestable::idHarvestable() {
3175         trigger = NULL;
3176         parentEnt = NULL;
3177 }
3178
3179 idHarvestable::~idHarvestable() {
3180         if ( trigger ) {
3181                 delete trigger;
3182                 trigger = NULL;
3183         }
3184 }
3185
3186 void idHarvestable::Spawn() {
3187         
3188         startTime = 0;
3189
3190         spawnArgs.GetFloat( "triggersize", "120", triggersize );
3191         spawnArgs.GetFloat( "give_delay", "3", giveDelay);
3192         giveDelay *= 1000;
3193         given = false;
3194
3195         removeDelay = spawnArgs.GetFloat( "remove_delay") * 1000.0f;
3196
3197         fxFollowPlayer = spawnArgs.GetBool("fx_follow_player", "1");
3198         fxOrient = spawnArgs.GetString("fx_orient");
3199
3200         
3201 }
3202
3203 void idHarvestable::Init(idEntity* parent) {
3204
3205         assert(parent);
3206         
3207         parentEnt = parent;
3208
3209         GetPhysics()->SetOrigin( parent->GetPhysics()->GetOrigin() );
3210         this->Bind(parent, true);
3211
3212         //Set the skin of the entity to the harvest skin
3213         idStr skin = parent->spawnArgs.GetString("skin_harvest", "");
3214         if(skin.Length()) {
3215                 parent->SetSkin(declManager->FindSkin(skin.c_str()));
3216         }
3217         
3218         idEntity* head;
3219         if(parent->IsType(idActor::Type)) {
3220                 idActor* withHead = (idActor*)parent;
3221                 head = withHead->GetHeadEntity();
3222         }
3223         if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
3224                 idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
3225                 head = withHead->head.GetEntity();
3226         }
3227         if(head) {
3228                 idStr headskin = parent->spawnArgs.GetString("skin_harvest_head", "");
3229                 if(headskin.Length()) {
3230                         head->SetSkin(declManager->FindSkin(headskin.c_str()));
3231                 }
3232         }
3233
3234         idStr sound = parent->spawnArgs.GetString("harvest_sound");
3235         if(sound.Length() > 0) {
3236                  parent->StartSound( sound.c_str(), SND_CHANNEL_ANY, 0, false, NULL);
3237         }
3238
3239
3240         PostEventMS( &EV_Harvest_SpawnHarvestTrigger, 0 );
3241 }
3242
3243 void idHarvestable::Save( idSaveGame *savefile ) const {
3244         savefile->WriteFloat( triggersize );
3245         savefile->WriteClipModel( trigger );
3246         savefile->WriteFloat( giveDelay );
3247         savefile->WriteFloat( removeDelay );
3248         savefile->WriteBool( given );
3249
3250         player.Save( savefile );
3251         savefile->WriteInt( startTime );
3252
3253         savefile->WriteBool( fxFollowPlayer );
3254         fx.Save( savefile );
3255         savefile->WriteString( fxOrient );
3256
3257         parentEnt.Save(savefile);
3258 }
3259
3260 void idHarvestable::Restore( idRestoreGame *savefile ) {
3261         savefile->ReadFloat( triggersize );
3262         savefile->ReadClipModel( trigger );
3263         savefile->ReadFloat( giveDelay );
3264         savefile->ReadFloat( removeDelay );
3265         savefile->ReadBool( given );
3266
3267         player.Restore( savefile );
3268         savefile->ReadInt( startTime );
3269
3270         savefile->ReadBool( fxFollowPlayer );
3271         fx.Restore( savefile );
3272         savefile->ReadString( fxOrient );
3273         
3274         parentEnt.Restore(savefile);
3275 }
3276
3277 void idHarvestable::SetParent(idEntity* parent) {
3278         parentEnt = parent;
3279 }
3280
3281 void idHarvestable::Think() {
3282
3283         idEntity* parent = parentEnt.GetEntity();
3284         if(!parent) {
3285                 return;
3286         }
3287
3288         //Update the orientation of the box
3289         if(trigger && parent && !parent->GetPhysics()->IsAtRest()) {
3290                 trigger->Link( gameLocal.clip, this, 0, parent->GetPhysics()->GetOrigin(), parent->GetPhysics()->GetAxis());
3291         }
3292
3293         if(startTime && gameLocal.slow.time - startTime > giveDelay && ! given) {
3294                 idPlayer *thePlayer = player.GetEntity();
3295
3296                 thePlayer->Give(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"));
3297                 thePlayer->harvest_lock = false;
3298                 given = true;
3299         }
3300
3301         if(startTime && gameLocal.slow.time - startTime > removeDelay) {
3302                 parent->PostEventMS( &EV_Remove, 0 );
3303                 PostEventMS( &EV_Remove, 0 );
3304         }
3305         
3306         if(fxFollowPlayer) {
3307                 idEntityFx* fxEnt = fx.GetEntity();
3308
3309                 if(fxEnt) {
3310                         idMat3 orientAxisLocal;
3311                         if(GetFxOrientationAxis(orientAxisLocal)) {
3312                                 //gameRenderWorld->DebugAxis(fxEnt->GetPhysics()->GetOrigin(), orientAxisLocal);
3313                                 fxEnt->GetPhysics()->SetAxis(orientAxisLocal);
3314                         }
3315                 }
3316         }
3317 }
3318
3319 /*
3320 ================
3321 idAFEntity_Harvest::Gib
3322 Called when the parent object has been gibbed.
3323 ================
3324 */
3325 void idHarvestable::Gib() {
3326         //Stop any looping sound that was playing
3327         idEntity* parent = parentEnt.GetEntity();
3328         if(parent) {
3329                 idStr sound = parent->spawnArgs.GetString("harvest_sound");
3330                 if(sound.Length() > 0) {
3331                         parent->StopSound(SND_CHANNEL_ANY, false);
3332                 }
3333         }
3334 }
3335
3336 /*
3337 ================
3338 idAFEntity_Harvest::BeginBurn
3339 ================
3340 */
3341 void idHarvestable::BeginBurn() {
3342         
3343         idEntity* parent = parentEnt.GetEntity();
3344         if(!parent) {
3345                 return;
3346         }
3347         
3348         if(!spawnArgs.GetBool("burn")) {
3349                 return;
3350         }
3351
3352
3353         //Switch Skins if the parent would like us to.
3354         idStr skin = parent->spawnArgs.GetString("skin_harvest_burn", "");
3355         if(skin.Length()) {
3356                 parent->SetSkin(declManager->FindSkin(skin.c_str()));
3357         }
3358         parent->GetRenderEntity()->noShadow = true;
3359         parent->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
3360
3361         idEntity* head;
3362         if(parent->IsType(idActor::Type)) {
3363                 idActor* withHead = (idActor*)parent;
3364                 head = withHead->GetHeadEntity();
3365         }
3366         if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
3367                 idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
3368                 head = withHead->head.GetEntity();
3369         }
3370         if(head) {
3371                 idStr headskin = parent->spawnArgs.GetString("skin_harvest_burn_head", "");
3372                 if(headskin.Length()) {
3373                         head->SetSkin(declManager->FindSkin(headskin.c_str()));
3374                 }
3375
3376                 head->GetRenderEntity()->noShadow = true;
3377                 head->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
3378         }
3379
3380         
3381         
3382 }
3383
3384 /*
3385 ================
3386 idAFEntity_Harvest::BeginFX
3387 ================
3388 */
3389 void idHarvestable::BeginFX() {
3390         if(strlen(spawnArgs.GetString("fx")) <= 0) {
3391                 return;
3392         }
3393
3394         idMat3* orientAxis = NULL;
3395         idMat3 orientAxisLocal;
3396
3397         if(GetFxOrientationAxis(orientAxisLocal)) {
3398                 orientAxis = &orientAxisLocal;
3399         }
3400         fx = idEntityFx::StartFx( spawnArgs.GetString("fx"), NULL, orientAxis, this, spawnArgs.GetBool("fx_bind") );
3401 }
3402
3403 /*
3404 ================
3405 idAFEntity_Harvest::CalcTriggerBounds
3406 ================
3407 */
3408 void idHarvestable::CalcTriggerBounds( float size, idBounds &bounds ) {
3409         
3410         idEntity* parent = parentEnt.GetEntity();
3411         if(!parent) {
3412                 return;
3413         }
3414
3415         //Simple trigger bounds is the absolute bounds of the AF plus a defined size
3416         bounds = parent->GetPhysics()->GetAbsBounds();
3417         bounds.ExpandSelf(size);
3418         bounds[0] -= parent->GetPhysics()->GetOrigin();
3419         bounds[1] -= parent->GetPhysics()->GetOrigin();
3420 }
3421
3422 bool idHarvestable::GetFxOrientationAxis(idMat3& mat) {
3423         
3424         idEntity* parent = parentEnt.GetEntity();
3425         if(!parent) {
3426                 return false;
3427         }
3428         
3429         idPlayer *thePlayer = player.GetEntity();
3430
3431         if(!fxOrient.Icmp("up")) {
3432                 //Orient up
3433                 idVec3 grav = parent->GetPhysics()->GetGravityNormal()*-1;
3434                 idVec3 left, up;
3435
3436                 grav.OrthogonalBasis(left, up);
3437                 idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, grav.x, grav.y, grav.z);
3438                 mat = temp;
3439
3440                 return true;
3441
3442         } else if(!fxOrient.Icmp("weapon")) {
3443                 //Orient the fx towards the muzzle of the weapon
3444                 jointHandle_t   joint;
3445                 idVec3  joint_origin;
3446                 idMat3  joint_axis;
3447
3448                 joint = thePlayer->weapon.GetEntity()->GetAnimator()->GetJointHandle( spawnArgs.GetString("fx_weapon_joint") );
3449                 if ( joint != INVALID_JOINT ) {
3450                         thePlayer->weapon.GetEntity()->GetJointWorldTransform( joint, gameLocal.slow.time, joint_origin, joint_axis );
3451                 } else {
3452                         joint_origin = thePlayer->GetPhysics()->GetOrigin();
3453                 }
3454
3455                 idVec3 toPlayer = joint_origin-parent->GetPhysics()->GetOrigin();
3456                 toPlayer.NormalizeFast();
3457
3458                 idVec3 left, up;
3459                 toPlayer.OrthogonalBasis(left, up);
3460                 idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
3461                 mat = temp;
3462
3463                 return true;
3464
3465         } else if(!fxOrient.Icmp("player")) {
3466                 //Orient the fx towards the eye of the player
3467                 idVec3 eye = thePlayer->GetEyePosition();
3468                 idVec3 toPlayer = eye-parent->GetPhysics()->GetOrigin();
3469                 
3470                 toPlayer.Normalize();
3471
3472                 idVec3 left, up;
3473                 up.Set(0, 1, 0);
3474                 left = toPlayer.Cross(up);
3475                 up = left.Cross(toPlayer);
3476
3477
3478                 //common->Printf("%.2f %.2f %.2f - %.2f %.2f %.2f - %.2f %.2f %.2f\n", toPlayer.x, toPlayer.y, toPlayer.z, left.x, left.y, left.z, up.x, up.y, up.z );
3479
3480                 idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
3481
3482                 mat = temp;
3483
3484                 return true;
3485         }
3486
3487         //Returning false indicates that the orientation is not used;
3488         return false;
3489 }
3490
3491 /*
3492 ================
3493 idAFEntity_Harvest::Event_SpawnHarvestTrigger
3494 ================
3495 */
3496 void idHarvestable::Event_SpawnHarvestTrigger( void ) {
3497         idBounds                bounds;
3498
3499         idEntity* parent = parentEnt.GetEntity();
3500         if(!parent) {
3501                 return;
3502         }
3503
3504         CalcTriggerBounds( triggersize, bounds );
3505
3506         // create a trigger clip model
3507         trigger = new idClipModel( idTraceModel( bounds ) );
3508         trigger->Link( gameLocal.clip, this, 255, parent->GetPhysics()->GetOrigin(), mat3_identity);
3509         trigger->SetContents( CONTENTS_TRIGGER );
3510
3511         startTime = 0;
3512 }
3513
3514 /*
3515 ================
3516 idAFEntity_Harvest::Event_Touch
3517 ================
3518 */
3519 void idHarvestable::Event_Touch( idEntity *other, trace_t *trace ) {
3520
3521         idEntity* parent = parentEnt.GetEntity();
3522         if(!parent) {
3523                 return;
3524         }
3525         if(parent->IsType(idAFEntity_Gibbable::Type)) {
3526                 idAFEntity_Gibbable* gibParent = (idAFEntity_Gibbable*)parent;
3527                 if(gibParent->IsGibbed())
3528                         return;
3529         }
3530
3531
3532         if(!startTime && other && other->IsType(idPlayer::Type)) {
3533                 idPlayer *thePlayer = static_cast<idPlayer *>(other);
3534
3535                 if(thePlayer->harvest_lock) {
3536                         //Don't harvest if the player is in mid harvest
3537                         return;
3538                 }
3539
3540                 player = thePlayer;
3541
3542                 bool okToGive = true;
3543                 idStr requiredWeapons = spawnArgs.GetString("required_weapons");
3544
3545                 if(requiredWeapons.Length() > 0) {
3546                         idStr playerWeap = thePlayer->GetCurrentWeapon();
3547                         if(playerWeap.Length() == 0 || requiredWeapons.Find(playerWeap, false) == -1) {
3548                                 okToGive = false;
3549                         }
3550                 }
3551
3552                 if(okToGive) {
3553                         if(thePlayer->CanGive(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"))) {
3554
3555                                 startTime = gameLocal.slow.time;
3556
3557                                 //Lock the player from harvesting to prevent multiple harvests when only one is needed
3558                                 thePlayer->harvest_lock = true;
3559
3560                                 idWeapon* weap = (idWeapon*)thePlayer->weapon.GetEntity();
3561                                 if(weap) {
3562                                         //weap->PostEventMS(&EV_Weapon_State, 0, "Charge", 8);
3563                                         weap->ProcessEvent(&EV_Weapon_State, "Charge", 8);
3564                                 }
3565
3566                                 BeginBurn();
3567                                 BeginFX();
3568
3569                                 //Stop any looping sound that was playing
3570                                 idStr sound = parent->spawnArgs.GetString("harvest_sound");
3571                                 if(sound.Length() > 0) {
3572                                         parent->StopSound(SND_CHANNEL_ANY, false);
3573                                 }
3574
3575                                 //Make the parent object non-solid
3576                                 parent->GetPhysics()->SetContents( 0 );
3577                                 parent->GetPhysics()->GetClipModel()->Unlink();
3578
3579                                 //Turn of the trigger so it doesn't process twice
3580                                 trigger->SetContents( 0 );
3581                         }
3582                 }
3583         }
3584 }
3585
3586
3587 /*
3588 ===============================================================================
3589
3590 idAFEntity_Harvest
3591
3592 ===============================================================================
3593 */
3594
3595 const idEventDef EV_Harvest_SpawnHarvestEntity( "<spawnHarvestEntity>", NULL );
3596
3597 CLASS_DECLARATION( idAFEntity_WithAttachedHead, idAFEntity_Harvest )
3598 EVENT( EV_Harvest_SpawnHarvestEntity,   idAFEntity_Harvest::Event_SpawnHarvestEntity )
3599 END_CLASS
3600
3601 /*
3602 ================
3603 idAFEntity_Harvest::idAFEntity_Harvest
3604 ================
3605 */
3606 idAFEntity_Harvest::idAFEntity_Harvest() {
3607         harvestEnt = NULL;
3608 }
3609
3610 /*
3611 ================
3612 idAFEntity_Harvest::~idAFEntity_Harvest
3613 ================
3614 */
3615 idAFEntity_Harvest::~idAFEntity_Harvest() {
3616         
3617         if ( harvestEnt.GetEntity() ) {
3618                 harvestEnt.GetEntity()->PostEventMS( &EV_Remove, 0 );
3619         }
3620         
3621 }
3622
3623 /*
3624 ================
3625 idAFEntity_Harvest::Save
3626 ================
3627 */
3628 void idAFEntity_Harvest::Save( idSaveGame *savefile ) const {
3629         harvestEnt.Save(savefile);
3630 }
3631
3632 /*
3633 ================
3634 idAFEntity_Harvest::Restore
3635 ================
3636 */
3637 void idAFEntity_Harvest::Restore( idRestoreGame *savefile ) {
3638         harvestEnt.Restore(savefile);
3639         //if(harvestEnt.GetEntity()) {
3640         //      harvestEnt.GetEntity()->SetParent(this);
3641         //}
3642 }
3643
3644 /*
3645 ================
3646 idAFEntity_Harvest::Spawn
3647 ================
3648 */
3649 void idAFEntity_Harvest::Spawn( void ) {
3650
3651         PostEventMS( &EV_Harvest_SpawnHarvestEntity, 0 );
3652 }
3653
3654 /*
3655 ================
3656 idAFEntity_Harvest::Think
3657 ================
3658 */
3659 void idAFEntity_Harvest::Think( void ) {
3660
3661         idAFEntity_WithAttachedHead::Think();
3662
3663 }
3664
3665 void idAFEntity_Harvest::Event_SpawnHarvestEntity( void ) {
3666
3667         const idDict *harvestDef = gameLocal.FindEntityDefDict( spawnArgs.GetString("def_harvest_type"), false );
3668         if ( harvestDef ) {
3669                 idEntity *temp;
3670                 gameLocal.SpawnEntityDef( *harvestDef, &temp, false );
3671                 harvestEnt = static_cast<idHarvestable *>(temp);
3672         }
3673
3674         if(harvestEnt.GetEntity()) {
3675                 //Let the harvest entity set itself up
3676                 harvestEnt.GetEntity()->Init(this);
3677                 harvestEnt.GetEntity()->BecomeActive( TH_THINK );
3678         }
3679 }
3680
3681 void idAFEntity_Harvest::Gib( const idVec3 &dir, const char *damageDefName ) {
3682         if(harvestEnt.GetEntity()) {
3683                 //Let the harvest ent know that we gibbed
3684                 harvestEnt.GetEntity()->Gib();
3685         }
3686         idAFEntity_WithAttachedHead::Gib(dir, damageDefName);
3687 }
3688
3689 #endif