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 "sys/platform.h"
30 #include "idlib/geometry/JointTransform.h"
31 #include "renderer/ModelManager.h"
32 
33 #include "gamesys/SysCvar.h"
34 #include "Item.h"
35 #include "Player.h"
36 #include "Fx.h"
37 #include "SmokeParticles.h"
38 
39 #include "AFEntity.h"
40 
41 /*
42 ===============================================================================
43 
44   idMultiModelAF
45 
46 ===============================================================================
47 */
48 
CLASS_DECLARATION(idEntity,idMultiModelAF)49 CLASS_DECLARATION( idEntity, idMultiModelAF )
50 END_CLASS
51 
52 /*
53 ================
54 idMultiModelAF::Spawn
55 ================
56 */
57 void idMultiModelAF::Spawn( void ) {
58 	physicsObj.SetSelf( this );
59 }
60 
61 /*
62 ================
63 idMultiModelAF::~idMultiModelAF
64 ================
65 */
~idMultiModelAF(void)66 idMultiModelAF::~idMultiModelAF( void ) {
67 	int i;
68 
69 	for ( i = 0; i < modelDefHandles.Num(); i++ ) {
70 		if ( modelDefHandles[i] != -1 ) {
71 			gameRenderWorld->FreeEntityDef( modelDefHandles[i] );
72 			modelDefHandles[i] = -1;
73 		}
74 	}
75 }
76 
77 /*
78 ================
79 idMultiModelAF::SetModelForId
80 ================
81 */
SetModelForId(int id,const idStr & modelName)82 void idMultiModelAF::SetModelForId( int id, const idStr &modelName ) {
83 	modelHandles.AssureSize( id+1, NULL );
84 	modelDefHandles.AssureSize( id+1, -1 );
85 	modelHandles[id] = renderModelManager->FindModel( modelName );
86 }
87 
88 /*
89 ================
90 idMultiModelAF::Present
91 ================
92 */
Present(void)93 void idMultiModelAF::Present( void ) {
94 	int i;
95 
96 	// don't present to the renderer if the entity hasn't changed
97 	if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
98 		return;
99 	}
100 	BecomeInactive( TH_UPDATEVISUALS );
101 
102 	for ( i = 0; i < modelHandles.Num(); i++ ) {
103 
104 		if ( !modelHandles[i] ) {
105 			continue;
106 		}
107 
108 		renderEntity.origin = physicsObj.GetOrigin( i );
109 		renderEntity.axis = physicsObj.GetAxis( i );
110 		renderEntity.hModel = modelHandles[i];
111 		renderEntity.bodyId = i;
112 
113 		// add to refresh list
114 		if ( modelDefHandles[i] == -1 ) {
115 			modelDefHandles[i] = gameRenderWorld->AddEntityDef( &renderEntity );
116 		} else {
117 			gameRenderWorld->UpdateEntityDef( modelDefHandles[i], &renderEntity );
118 		}
119 	}
120 }
121 
122 /*
123 ================
124 idMultiModelAF::Think
125 ================
126 */
Think(void)127 void idMultiModelAF::Think( void ) {
128 	RunPhysics();
129 	Present();
130 }
131 
132 
133 /*
134 ===============================================================================
135 
136   idChain
137 
138 ===============================================================================
139 */
140 
CLASS_DECLARATION(idMultiModelAF,idChain)141 CLASS_DECLARATION( idMultiModelAF, idChain )
142 END_CLASS
143 
144 /*
145 ================
146 idChain::BuildChain
147 
148   builds a chain hanging down from the ceiling
149   the highest link is a child of the link below it etc.
150   this allows an object to be attached to multiple chains while keeping a single tree structure
151 ================
152 */
153 void idChain::BuildChain( const idStr &name, const idVec3 &origin, float linkLength, float linkWidth, float density, int numLinks, bool bindToWorld ) {
154 	int i;
155 	float halfLinkLength = linkLength * 0.5f;
156 	idTraceModel trm;
157 	idClipModel *clip;
158 	idAFBody *body, *lastBody;
159 	idAFConstraint_BallAndSocketJoint *bsj;
160 	idAFConstraint_UniversalJoint *uj;
161 	idVec3 org;
162 
163 	// create a trace model
164 	trm = idTraceModel( linkLength, linkWidth );
165 	trm.Translate( -trm.offset );
166 
167 	org = origin - idVec3( 0, 0, halfLinkLength );
168 
169 	lastBody = NULL;
170 	for ( i = 0; i < numLinks; i++ ) {
171 
172 		// add body
173 		clip = new idClipModel( trm );
174 		clip->SetContents( CONTENTS_SOLID );
175 		clip->Link( gameLocal.clip, this, 0, org, mat3_identity );
176 		body = new idAFBody( name + idStr(i), clip, density );
177 		physicsObj.AddBody( body );
178 
179 		// visual model for body
180 		SetModelForId( physicsObj.GetBodyId( body ), spawnArgs.GetString( "model" ) );
181 
182 		// add constraint
183 		if ( bindToWorld ) {
184 			if ( !lastBody ) {
185 				uj = new idAFConstraint_UniversalJoint( name + idStr(i), body, lastBody );
186 				uj->SetShafts( idVec3( 0, 0, -1 ), idVec3( 0, 0, 1 ) );
187 				//uj->SetConeLimit( idVec3( 0, 0, -1 ), 30.0f );
188 				//uj->SetPyramidLimit( idVec3( 0, 0, -1 ), idVec3( 1, 0, 0 ), 90.0f, 30.0f );
189 			}
190 			else {
191 				uj = new idAFConstraint_UniversalJoint( name + idStr(i), lastBody, body );
192 				uj->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) );
193 				//uj->SetConeLimit( idVec3( 0, 0, 1 ), 30.0f );
194 			}
195 			uj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
196 			uj->SetFriction( 0.9f );
197 			physicsObj.AddConstraint( uj );
198 		}
199 		else {
200 			if ( lastBody ) {
201 				bsj = new idAFConstraint_BallAndSocketJoint( "joint" + idStr(i), lastBody, body );
202 				bsj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
203 				bsj->SetConeLimit( idVec3( 0, 0, 1 ), 60.0f, idVec3( 0, 0, 1 ) );
204 				physicsObj.AddConstraint( bsj );
205 			}
206 		}
207 
208 		org[2] -= linkLength;
209 
210 		lastBody = body;
211 	}
212 }
213 
214 /*
215 ================
216 idChain::Spawn
217 ================
218 */
Spawn(void)219 void idChain::Spawn( void ) {
220 	int numLinks;
221 	float length, linkLength, linkWidth, density;
222 	bool drop;
223 	idVec3 origin;
224 
225 	spawnArgs.GetBool( "drop", "0", drop );
226 	spawnArgs.GetInt( "links", "3", numLinks );
227 	spawnArgs.GetFloat( "length", idStr( numLinks * 32.0f ), length );
228 	spawnArgs.GetFloat( "width", "8", linkWidth );
229 	spawnArgs.GetFloat( "density", "0.2", density );
230 	linkLength = length / numLinks;
231 	origin = GetPhysics()->GetOrigin();
232 
233 	// initialize physics
234 	physicsObj.SetSelf( this );
235 	physicsObj.SetGravity( gameLocal.GetGravity() );
236 	physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY );
237 	SetPhysics( &physicsObj );
238 
239 	BuildChain( "link", origin, linkLength, linkWidth, density, numLinks, !drop );
240 }
241 
242 /*
243 ===============================================================================
244 
245   idAFAttachment
246 
247 ===============================================================================
248 */
249 
CLASS_DECLARATION(idAnimatedEntity,idAFAttachment)250 CLASS_DECLARATION( idAnimatedEntity, idAFAttachment )
251 END_CLASS
252 
253 /*
254 =====================
255 idAFAttachment::idAFAttachment
256 =====================
257 */
258 idAFAttachment::idAFAttachment( void ) {
259 	body			= NULL;
260 	combatModel		= NULL;
261 	idleAnim		= 0;
262 	attachJoint		= INVALID_JOINT;
263 }
264 
265 /*
266 =====================
267 idAFAttachment::~idAFAttachment
268 =====================
269 */
~idAFAttachment(void)270 idAFAttachment::~idAFAttachment( void ) {
271 
272 	StopSound( SND_CHANNEL_ANY, false );
273 
274 	delete combatModel;
275 	combatModel = NULL;
276 }
277 
278 /*
279 =====================
280 idAFAttachment::Spawn
281 =====================
282 */
Spawn(void)283 void idAFAttachment::Spawn( void ) {
284 	idleAnim = animator.GetAnim( "idle" );
285 }
286 
287 /*
288 =====================
289 idAFAttachment::SetBody
290 =====================
291 */
SetBody(idEntity * bodyEnt,const char * model,jointHandle_t attachJoint)292 void idAFAttachment::SetBody( idEntity *bodyEnt, const char *model, jointHandle_t attachJoint ) {
293 	bool bleed;
294 
295 	body = bodyEnt;
296 	this->attachJoint = attachJoint;
297 	SetModel( model );
298 	fl.takedamage = true;
299 
300 	bleed = body->spawnArgs.GetBool( "bleed" );
301 	spawnArgs.SetBool( "bleed", bleed );
302 }
303 
304 /*
305 =====================
306 idAFAttachment::ClearBody
307 =====================
308 */
ClearBody(void)309 void idAFAttachment::ClearBody( void ) {
310 	body = NULL;
311 	attachJoint = INVALID_JOINT;
312 	Hide();
313 }
314 
315 /*
316 =====================
317 idAFAttachment::GetBody
318 =====================
319 */
GetBody(void) const320 idEntity *idAFAttachment::GetBody( void ) const {
321 	return body;
322 }
323 
324 /*
325 ================
326 idAFAttachment::Save
327 
328 archive object for savegame file
329 ================
330 */
Save(idSaveGame * savefile) const331 void idAFAttachment::Save( idSaveGame *savefile ) const {
332 	savefile->WriteObject( body );
333 	savefile->WriteInt( idleAnim );
334 	savefile->WriteJoint( attachJoint );
335 }
336 
337 /*
338 ================
339 idAFAttachment::Restore
340 
341 unarchives object from save game file
342 ================
343 */
Restore(idRestoreGame * savefile)344 void idAFAttachment::Restore( idRestoreGame *savefile ) {
345 	savefile->ReadObject( reinterpret_cast<idClass *&>( body ) );
346 	savefile->ReadInt( idleAnim );
347 	savefile->ReadJoint( attachJoint );
348 
349 	SetCombatModel();
350 	LinkCombat();
351 }
352 
353 /*
354 ================
355 idAFAttachment::Hide
356 ================
357 */
Hide(void)358 void idAFAttachment::Hide( void ) {
359 	idEntity::Hide();
360 	UnlinkCombat();
361 }
362 
363 /*
364 ================
365 idAFAttachment::Show
366 ================
367 */
Show(void)368 void idAFAttachment::Show( void ) {
369 	idEntity::Show();
370 	LinkCombat();
371 }
372 
373 /*
374 ============
375 idAFAttachment::Damage
376 
377 Pass damage to body at the bindjoint
378 ============
379 */
Damage(idEntity * inflictor,idEntity * attacker,const idVec3 & dir,const char * damageDefName,const float damageScale,const int location)380 void idAFAttachment::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
381 	const char *damageDefName, const float damageScale, const int location ) {
382 
383 	if ( body ) {
384 		body->Damage( inflictor, attacker, dir, damageDefName, damageScale, attachJoint );
385 	}
386 }
387 
388 /*
389 ================
390 idAFAttachment::AddDamageEffect
391 ================
392 */
AddDamageEffect(const trace_t & collision,const idVec3 & velocity,const char * damageDefName)393 void idAFAttachment::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
394 	if ( body ) {
395 		trace_t c = collision;
396 		c.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint );
397 		body->AddDamageEffect( c, velocity, damageDefName );
398 	}
399 }
400 
401 /*
402 ================
403 idAFAttachment::GetImpactInfo
404 ================
405 */
GetImpactInfo(idEntity * ent,int id,const idVec3 & point,impactInfo_t * info)406 void idAFAttachment::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
407 	if ( body ) {
408 		body->GetImpactInfo( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, info );
409 	} else {
410 		idEntity::GetImpactInfo( ent, id, point, info );
411 	}
412 }
413 
414 /*
415 ================
416 idAFAttachment::ApplyImpulse
417 ================
418 */
ApplyImpulse(idEntity * ent,int id,const idVec3 & point,const idVec3 & impulse)419 void idAFAttachment::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
420 	if ( body ) {
421 		body->ApplyImpulse( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, impulse );
422 	} else {
423 		idEntity::ApplyImpulse( ent, id, point, impulse );
424 	}
425 }
426 
427 /*
428 ================
429 idAFAttachment::AddForce
430 ================
431 */
AddForce(idEntity * ent,int id,const idVec3 & point,const idVec3 & force)432 void idAFAttachment::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
433 	if ( body ) {
434 		body->AddForce( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, force );
435 	} else {
436 		idEntity::AddForce( ent, id, point, force );
437 	}
438 }
439 
440 /*
441 ================
442 idAFAttachment::PlayIdleAnim
443 ================
444 */
PlayIdleAnim(int blendTime)445 void idAFAttachment::PlayIdleAnim( int blendTime ) {
446 	if ( idleAnim && ( idleAnim != animator.CurrentAnim( ANIMCHANNEL_ALL )->AnimNum() ) ) {
447 		animator.CycleAnim( ANIMCHANNEL_ALL, idleAnim, gameLocal.time, blendTime );
448 	}
449 }
450 
451 /*
452 ================
453 idAfAttachment::Think
454 ================
455 */
Think(void)456 void idAFAttachment::Think( void ) {
457 	idAnimatedEntity::Think();
458 	if ( thinkFlags & TH_UPDATEPARTICLES ) {
459 		UpdateDamageEffects();
460 	}
461 }
462 
463 /*
464 ================
465 idAFAttachment::SetCombatModel
466 ================
467 */
SetCombatModel(void)468 void idAFAttachment::SetCombatModel( void ) {
469 	if ( combatModel ) {
470 		combatModel->Unlink();
471 		combatModel->LoadModel( modelDefHandle );
472 	} else {
473 		combatModel = new idClipModel( modelDefHandle );
474 	}
475 	combatModel->SetOwner( body );
476 }
477 
478 /*
479 ================
480 idAFAttachment::GetCombatModel
481 ================
482 */
GetCombatModel(void) const483 idClipModel *idAFAttachment::GetCombatModel( void ) const {
484 	return combatModel;
485 }
486 
487 /*
488 ================
489 idAFAttachment::LinkCombat
490 ================
491 */
LinkCombat(void)492 void idAFAttachment::LinkCombat( void ) {
493 	if ( fl.hidden ) {
494 		return;
495 	}
496 
497 	if ( combatModel ) {
498 		combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
499 	}
500 }
501 
502 /*
503 ================
504 idAFAttachment::UnlinkCombat
505 ================
506 */
UnlinkCombat(void)507 void idAFAttachment::UnlinkCombat( void ) {
508 	if ( combatModel ) {
509 		combatModel->Unlink();
510 	}
511 }
512 
513 
514 /*
515 ===============================================================================
516 
517   idAFEntity_Base
518 
519 ===============================================================================
520 */
521 
522 const idEventDef EV_SetConstraintPosition( "SetConstraintPosition", "sv" );
523 
524 CLASS_DECLARATION( idAnimatedEntity, idAFEntity_Base )
525 	EVENT( EV_SetConstraintPosition,	idAFEntity_Base::Event_SetConstraintPosition )
526 END_CLASS
527 
528 static const float BOUNCE_SOUND_MIN_VELOCITY	= 80.0f;
529 static const float BOUNCE_SOUND_MAX_VELOCITY	= 200.0f;
530 
531 /*
532 ================
533 idAFEntity_Base::idAFEntity_Base
534 ================
535 */
idAFEntity_Base(void)536 idAFEntity_Base::idAFEntity_Base( void ) {
537 	combatModel = NULL;
538 	combatModelContents = 0;
539 	nextSoundTime = 0;
540 	spawnOrigin.Zero();
541 	spawnAxis.Identity();
542 }
543 
544 /*
545 ================
546 idAFEntity_Base::~idAFEntity_Base
547 ================
548 */
~idAFEntity_Base(void)549 idAFEntity_Base::~idAFEntity_Base( void ) {
550 	delete combatModel;
551 	combatModel = NULL;
552 }
553 
554 /*
555 ================
556 idAFEntity_Base::Save
557 ================
558 */
Save(idSaveGame * savefile) const559 void idAFEntity_Base::Save( idSaveGame *savefile ) const {
560 	savefile->WriteInt( combatModelContents );
561 	savefile->WriteClipModel( combatModel );
562 	savefile->WriteVec3( spawnOrigin );
563 	savefile->WriteMat3( spawnAxis );
564 	savefile->WriteInt( nextSoundTime );
565 	af.Save( savefile );
566 }
567 
568 /*
569 ================
570 idAFEntity_Base::Restore
571 ================
572 */
Restore(idRestoreGame * savefile)573 void idAFEntity_Base::Restore( idRestoreGame *savefile ) {
574 	savefile->ReadInt( combatModelContents );
575 	savefile->ReadClipModel( combatModel );
576 	savefile->ReadVec3( spawnOrigin );
577 	savefile->ReadMat3( spawnAxis );
578 	savefile->ReadInt( nextSoundTime );
579 	LinkCombat();
580 
581 	af.Restore( savefile );
582 }
583 
584 /*
585 ================
586 idAFEntity_Base::Spawn
587 ================
588 */
Spawn(void)589 void idAFEntity_Base::Spawn( void ) {
590 	spawnOrigin = GetPhysics()->GetOrigin();
591 	spawnAxis = GetPhysics()->GetAxis();
592 	nextSoundTime = 0;
593 }
594 
595 /*
596 ================
597 idAFEntity_Base::LoadAF
598 ================
599 */
LoadAF(void)600 bool idAFEntity_Base::LoadAF( void ) {
601 	idStr fileName;
602 
603 	if ( !spawnArgs.GetString( "articulatedFigure", "*unknown*", fileName ) ) {
604 		return false;
605 	}
606 
607 	af.SetAnimator( GetAnimator() );
608 	if ( !af.Load( this, fileName ) ) {
609 		gameLocal.Error( "idAFEntity_Base::LoadAF: Couldn't load af file '%s' on entity '%s'", fileName.c_str(), name.c_str() );
610 	}
611 
612 	af.Start();
613 
614 	af.GetPhysics()->Rotate( spawnAxis.ToRotation() );
615 	af.GetPhysics()->Translate( spawnOrigin );
616 
617 	LoadState( spawnArgs );
618 
619 	af.UpdateAnimation();
620 	animator.CreateFrame( gameLocal.time, true );
621 	UpdateVisuals();
622 
623 	return true;
624 }
625 
626 /*
627 ================
628 idAFEntity_Base::Think
629 ================
630 */
Think(void)631 void idAFEntity_Base::Think( void ) {
632 	RunPhysics();
633 	UpdateAnimation();
634 	if ( thinkFlags & TH_UPDATEVISUALS ) {
635 		Present();
636 		LinkCombat();
637 	}
638 }
639 
640 /*
641 ================
642 idAFEntity_Base::BodyForClipModelId
643 ================
644 */
BodyForClipModelId(int id) const645 int idAFEntity_Base::BodyForClipModelId( int id ) const {
646 	return af.BodyForClipModelId( id );
647 }
648 
649 /*
650 ================
651 idAFEntity_Base::SaveState
652 ================
653 */
SaveState(idDict & args) const654 void idAFEntity_Base::SaveState( idDict &args ) const {
655 	const idKeyValue *kv;
656 
657 	// save the ragdoll pose
658 	af.SaveState( args );
659 
660 	// save all the bind constraints
661 	kv = spawnArgs.MatchPrefix( "bindConstraint ", NULL );
662 	while ( kv ) {
663 		args.Set( kv->GetKey(), kv->GetValue() );
664 		kv = spawnArgs.MatchPrefix( "bindConstraint ", kv );
665 	}
666 
667 	// save the bind if it exists
668 	kv = spawnArgs.FindKey( "bind" );
669 	if ( kv ) {
670 		args.Set( kv->GetKey(), kv->GetValue() );
671 	}
672 	kv = spawnArgs.FindKey( "bindToJoint" );
673 	if ( kv ) {
674 		args.Set( kv->GetKey(), kv->GetValue() );
675 	}
676 	kv = spawnArgs.FindKey( "bindToBody" );
677 	if ( kv ) {
678 		args.Set( kv->GetKey(), kv->GetValue() );
679 	}
680 }
681 
682 /*
683 ================
684 idAFEntity_Base::LoadState
685 ================
686 */
LoadState(const idDict & args)687 void idAFEntity_Base::LoadState( const idDict &args ) {
688 	af.LoadState( args );
689 }
690 
691 /*
692 ================
693 idAFEntity_Base::AddBindConstraints
694 ================
695 */
AddBindConstraints(void)696 void idAFEntity_Base::AddBindConstraints( void ) {
697 	af.AddBindConstraints();
698 }
699 
700 /*
701 ================
702 idAFEntity_Base::RemoveBindConstraints
703 ================
704 */
RemoveBindConstraints(void)705 void idAFEntity_Base::RemoveBindConstraints( void ) {
706 	af.RemoveBindConstraints();
707 }
708 
709 /*
710 ================
711 idAFEntity_Base::GetImpactInfo
712 ================
713 */
GetImpactInfo(idEntity * ent,int id,const idVec3 & point,impactInfo_t * info)714 void idAFEntity_Base::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
715 	if ( af.IsActive() ) {
716 		af.GetImpactInfo( ent, id, point, info );
717 	} else {
718 		idEntity::GetImpactInfo( ent, id, point, info );
719 	}
720 }
721 
722 /*
723 ================
724 idAFEntity_Base::ApplyImpulse
725 ================
726 */
ApplyImpulse(idEntity * ent,int id,const idVec3 & point,const idVec3 & impulse)727 void idAFEntity_Base::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
728 	if ( af.IsLoaded() ) {
729 		af.ApplyImpulse( ent, id, point, impulse );
730 	}
731 	if ( !af.IsActive() ) {
732 		idEntity::ApplyImpulse( ent, id, point, impulse );
733 	}
734 }
735 
736 /*
737 ================
738 idAFEntity_Base::AddForce
739 ================
740 */
AddForce(idEntity * ent,int id,const idVec3 & point,const idVec3 & force)741 void idAFEntity_Base::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
742 	if ( af.IsLoaded() ) {
743 		af.AddForce( ent, id, point, force );
744 	}
745 	if ( !af.IsActive() ) {
746 		idEntity::AddForce( ent, id, point, force );
747 	}
748 }
749 
750 /*
751 ================
752 idAFEntity_Base::Collide
753 ================
754 */
Collide(const trace_t & collision,const idVec3 & velocity)755 bool idAFEntity_Base::Collide( const trace_t &collision, const idVec3 &velocity ) {
756 	float v, f;
757 
758 	if ( af.IsActive() ) {
759 		v = -( velocity * collision.c.normal );
760 		if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
761 			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 ) );
762 			if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) {
763 				// don't set the volume unless there is a bounce sound as it overrides the entire channel
764 				// which causes footsteps on ai's to not honor their shader parms
765 				SetSoundVolume( f );
766 			}
767 			nextSoundTime = gameLocal.time + 500;
768 		}
769 	}
770 
771 	return false;
772 }
773 
774 /*
775 ================
776 idAFEntity_Base::GetPhysicsToVisualTransform
777 ================
778 */
GetPhysicsToVisualTransform(idVec3 & origin,idMat3 & axis)779 bool idAFEntity_Base::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
780 	if ( af.IsActive() ) {
781 		af.GetPhysicsToVisualTransform( origin, axis );
782 		return true;
783 	}
784 	return idEntity::GetPhysicsToVisualTransform( origin, axis );
785 }
786 
787 /*
788 ================
789 idAFEntity_Base::UpdateAnimationControllers
790 ================
791 */
UpdateAnimationControllers(void)792 bool idAFEntity_Base::UpdateAnimationControllers( void ) {
793 	if ( af.IsActive() ) {
794 		if ( af.UpdateAnimation() ) {
795 			return true;
796 		}
797 	}
798 	return false;
799 }
800 
801 /*
802 ================
803 idAFEntity_Base::SetCombatModel
804 ================
805 */
SetCombatModel(void)806 void idAFEntity_Base::SetCombatModel( void ) {
807 	if ( combatModel ) {
808 		combatModel->Unlink();
809 		combatModel->LoadModel( modelDefHandle );
810 	} else {
811 		combatModel = new idClipModel( modelDefHandle );
812 	}
813 }
814 
815 /*
816 ================
817 idAFEntity_Base::GetCombatModel
818 ================
819 */
GetCombatModel(void) const820 idClipModel *idAFEntity_Base::GetCombatModel( void ) const {
821 	return combatModel;
822 }
823 
824 /*
825 ================
826 idAFEntity_Base::SetCombatContents
827 ================
828 */
SetCombatContents(bool enable)829 void idAFEntity_Base::SetCombatContents( bool enable ) {
830 	assert( combatModel );
831 	if ( enable && combatModelContents ) {
832 		assert( !combatModel->GetContents() );
833 		combatModel->SetContents( combatModelContents );
834 		combatModelContents = 0;
835 	} else if ( !enable && combatModel->GetContents() ) {
836 		assert( !combatModelContents );
837 		combatModelContents = combatModel->GetContents();
838 		combatModel->SetContents( 0 );
839 	}
840 }
841 
842 /*
843 ================
844 idAFEntity_Base::LinkCombat
845 ================
846 */
LinkCombat(void)847 void idAFEntity_Base::LinkCombat( void ) {
848 	if ( fl.hidden ) {
849 		return;
850 	}
851 	if ( combatModel ) {
852 		combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
853 	}
854 }
855 
856 /*
857 ================
858 idAFEntity_Base::UnlinkCombat
859 ================
860 */
UnlinkCombat(void)861 void idAFEntity_Base::UnlinkCombat( void ) {
862 	if ( combatModel ) {
863 		combatModel->Unlink();
864 	}
865 }
866 
867 /*
868 ================
869 idAFEntity_Base::FreeModelDef
870 ================
871 */
FreeModelDef(void)872 void idAFEntity_Base::FreeModelDef( void ) {
873 	UnlinkCombat();
874 	idEntity::FreeModelDef();
875 }
876 
877 /*
878 ===============
879 idAFEntity_Base::ShowEditingDialog
880 ===============
881 */
ShowEditingDialog(void)882 void idAFEntity_Base::ShowEditingDialog( void ) {
883 	common->InitTool( EDITOR_AF, &spawnArgs );
884 }
885 
886 /*
887 ================
888 idAFEntity_Base::DropAFs
889 
890   The entity should have the following key/value pairs set:
891 	"def_drop<type>AF"		"af def"
892 	"drop<type>Skin"		"skin name"
893   To drop multiple articulated figures the following key/value pairs can be used:
894 	"def_drop<type>AF*"		"af def"
895   where * is an aribtrary string.
896 ================
897 */
DropAFs(idEntity * ent,const char * type,idList<idEntity * > * list)898 void idAFEntity_Base::DropAFs( idEntity *ent, const char *type, idList<idEntity *> *list ) {
899 	const idKeyValue *kv;
900 	const char *skinName;
901 	idEntity *newEnt;
902 	idAFEntity_Base *af;
903 	idDict args;
904 	const idDeclSkin *skin;
905 
906 	// drop the articulated figures
907 	kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), NULL );
908 	while ( kv ) {
909 
910 		args.Set( "classname", kv->GetValue() );
911 		gameLocal.SpawnEntityDef( args, &newEnt );
912 
913 		if ( newEnt && newEnt->IsType( idAFEntity_Base::Type ) ) {
914 			af = static_cast<idAFEntity_Base *>(newEnt);
915 			af->GetPhysics()->SetOrigin( ent->GetPhysics()->GetOrigin() );
916 			af->GetPhysics()->SetAxis( ent->GetPhysics()->GetAxis() );
917 			af->af.SetupPose( ent, gameLocal.time );
918 			if ( list ) {
919 				list->Append( af );
920 			}
921 		}
922 
923 		kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), kv );
924 	}
925 
926 	// change the skin to hide all the dropped articulated figures
927 	skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) );
928 	if ( skinName[0] ) {
929 		skin = declManager->FindSkin( skinName );
930 		ent->SetSkin( skin );
931 	}
932 }
933 
934 /*
935 ================
936 idAFEntity_Base::Event_SetConstraintPosition
937 ================
938 */
Event_SetConstraintPosition(const char * name,const idVec3 & pos)939 void idAFEntity_Base::Event_SetConstraintPosition( const char *name, const idVec3 &pos ) {
940 	af.SetConstraintPosition( name, pos );
941 }
942 
943 /*
944 ===============================================================================
945 
946 idAFEntity_Gibbable
947 
948 ===============================================================================
949 */
950 
951 const idEventDef EV_Gib( "gib", "s" );
952 const idEventDef EV_Gibbed( "<gibbed>" );
953 
CLASS_DECLARATION(idAFEntity_Base,idAFEntity_Gibbable)954 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Gibbable )
955 	EVENT( EV_Gib,		idAFEntity_Gibbable::Event_Gib )
956 	EVENT( EV_Gibbed,	idAFEntity_Base::Event_Remove )
957 END_CLASS
958 
959 
960 /*
961 ================
962 idAFEntity_Gibbable::idAFEntity_Gibbable
963 ================
964 */
965 idAFEntity_Gibbable::idAFEntity_Gibbable( void ) {
966 	skeletonModel = NULL;
967 	skeletonModelDefHandle = -1;
968 	gibbed = false;
969 #ifdef _D3XP
970 	wasThrown = false;
971 #endif
972 }
973 
974 /*
975 ================
976 idAFEntity_Gibbable::~idAFEntity_Gibbable
977 ================
978 */
~idAFEntity_Gibbable()979 idAFEntity_Gibbable::~idAFEntity_Gibbable() {
980 	if ( skeletonModelDefHandle != -1 ) {
981 		gameRenderWorld->FreeEntityDef( skeletonModelDefHandle );
982 		skeletonModelDefHandle = -1;
983 	}
984 }
985 
986 /*
987 ================
988 idAFEntity_Gibbable::Save
989 ================
990 */
Save(idSaveGame * savefile) const991 void idAFEntity_Gibbable::Save( idSaveGame *savefile ) const {
992 	savefile->WriteBool( gibbed );
993 	savefile->WriteBool( combatModel != NULL );
994 #ifdef _D3XP
995 	savefile->WriteBool( wasThrown );
996 #endif
997 }
998 
999 /*
1000 ================
1001 idAFEntity_Gibbable::Restore
1002 ================
1003 */
Restore(idRestoreGame * savefile)1004 void idAFEntity_Gibbable::Restore( idRestoreGame *savefile ) {
1005 	bool hasCombatModel;
1006 
1007 	savefile->ReadBool( gibbed );
1008 	savefile->ReadBool( hasCombatModel );
1009 #ifdef _D3XP
1010 	savefile->ReadBool( wasThrown );
1011 #endif
1012 
1013 	InitSkeletonModel();
1014 
1015 	if ( hasCombatModel ) {
1016 		SetCombatModel();
1017 		LinkCombat();
1018 	}
1019 }
1020 
1021 /*
1022 ================
1023 idAFEntity_Gibbable::Spawn
1024 ================
1025 */
Spawn(void)1026 void idAFEntity_Gibbable::Spawn( void ) {
1027 	InitSkeletonModel();
1028 
1029 	gibbed = false;
1030 #ifdef _D3XP
1031 	wasThrown = false;
1032 #endif
1033 }
1034 
1035 /*
1036 ================
1037 idAFEntity_Gibbable::InitSkeletonModel
1038 ================
1039 */
InitSkeletonModel(void)1040 void idAFEntity_Gibbable::InitSkeletonModel( void ) {
1041 	const char *modelName;
1042 	const idDeclModelDef *modelDef;
1043 
1044 	skeletonModel = NULL;
1045 	skeletonModelDefHandle = -1;
1046 
1047 	modelName = spawnArgs.GetString( "model_gib" );
1048 
1049 	modelDef = NULL;
1050 	if ( modelName[0] != '\0' ) {
1051 		modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
1052 		if ( modelDef ) {
1053 			skeletonModel = modelDef->ModelHandle();
1054 		} else {
1055 			skeletonModel = renderModelManager->FindModel( modelName );
1056 		}
1057 		if ( skeletonModel != NULL && renderEntity.hModel != NULL ) {
1058 			if ( skeletonModel->NumJoints() != renderEntity.hModel->NumJoints() ) {
1059 				gameLocal.Error( "gib model '%s' has different number of joints than model '%s'",
1060 									skeletonModel->Name(), renderEntity.hModel->Name() );
1061 			}
1062 		}
1063 	}
1064 }
1065 
1066 /*
1067 ================
1068 idAFEntity_Gibbable::Present
1069 ================
1070 */
Present(void)1071 void idAFEntity_Gibbable::Present( void ) {
1072 	renderEntity_t skeleton;
1073 
1074 	if ( !gameLocal.isNewFrame ) {
1075 		return;
1076 	}
1077 
1078 	// don't present to the renderer if the entity hasn't changed
1079 	if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
1080 		return;
1081 	}
1082 
1083 	// update skeleton model
1084 	if ( gibbed && !IsHidden() && skeletonModel != NULL ) {
1085 		skeleton = renderEntity;
1086 		skeleton.hModel = skeletonModel;
1087 		// add to refresh list
1088 		if ( skeletonModelDefHandle == -1 ) {
1089 			skeletonModelDefHandle = gameRenderWorld->AddEntityDef( &skeleton );
1090 		} else {
1091 			gameRenderWorld->UpdateEntityDef( skeletonModelDefHandle, &skeleton );
1092 		}
1093 	}
1094 
1095 	idEntity::Present();
1096 }
1097 
1098 /*
1099 ================
1100 idAFEntity_Gibbable::Damage
1101 ================
1102 */
Damage(idEntity * inflictor,idEntity * attacker,const idVec3 & dir,const char * damageDefName,const float damageScale,const int location)1103 void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
1104 	if ( !fl.takedamage ) {
1105 		return;
1106 	}
1107 	idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
1108 	if ( health < -20 && spawnArgs.GetBool( "gib" ) ) {
1109 		Gib( dir, damageDefName );
1110 	}
1111 }
1112 
1113 #ifdef _D3XP
1114 /*
1115 =====================
1116 idAFEntity_Gibbable::SetThrown
1117 =====================
1118 */
SetThrown(bool isThrown)1119 void idAFEntity_Gibbable::SetThrown( bool isThrown ) {
1120 
1121 	if ( isThrown ) {
1122 		int i, num = af.GetPhysics()->GetNumBodies();
1123 
1124 		for ( i=0; i<num; i++ ) {
1125 			idAFBody *body;
1126 
1127 			body = af.GetPhysics()->GetBody( i );
1128 			body->SetClipMask( MASK_MONSTERSOLID );
1129 		}
1130 	}
1131 
1132 	wasThrown = isThrown;
1133 }
1134 
1135 /*
1136 =====================
1137 idAFEntity_Gibbable::Collide
1138 =====================
1139 */
Collide(const trace_t & collision,const idVec3 & velocity)1140 bool idAFEntity_Gibbable::Collide( const trace_t &collision, const idVec3 &velocity ) {
1141 
1142 	if ( !gibbed && wasThrown ) {
1143 
1144 		// Everything gibs (if possible)
1145 		if ( spawnArgs.GetBool( "gib" ) ) {
1146 			idEntity	*ent;
1147 
1148 			ent = gameLocal.entities[ collision.c.entityNum ];
1149 			if ( ent->fl.takedamage ) {
1150 				ent->Damage( this, gameLocal.GetLocalPlayer(), collision.c.normal, "damage_thrown_ragdoll", 1.f, CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) );
1151 			}
1152 
1153 			idVec3 vel = velocity;
1154 			vel.NormalizeFast();
1155 			Gib( vel, "damage_gib" );
1156 		}
1157 	}
1158 
1159 	return idAFEntity_Base::Collide( collision, velocity );
1160 }
1161 #endif
1162 
1163 /*
1164 =====================
1165 idAFEntity_Gibbable::SpawnGibs
1166 =====================
1167 */
SpawnGibs(const idVec3 & dir,const char * damageDefName)1168 void idAFEntity_Gibbable::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
1169 	int i;
1170 	bool gibNonSolid;
1171 	idVec3 entityCenter, velocity;
1172 	idList<idEntity *> list;
1173 
1174 	assert( !gameLocal.isClient );
1175 
1176 	const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1177 	if ( !damageDef ) {
1178 		gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1179 	}
1180 
1181 	// spawn gib articulated figures
1182 	idAFEntity_Base::DropAFs( this, "gib", &list );
1183 
1184 	// spawn gib items
1185 	idMoveableItem::DropItems( this, "gib", &list );
1186 
1187 	// blow out the gibs in the given direction away from the center of the entity
1188 	entityCenter = GetPhysics()->GetAbsBounds().GetCenter();
1189 	gibNonSolid = damageDef->GetBool( "gibNonSolid" );
1190 	for ( i = 0; i < list.Num(); i++ ) {
1191 		if ( gibNonSolid ) {
1192 			list[i]->GetPhysics()->SetContents( 0 );
1193 			list[i]->GetPhysics()->SetClipMask( 0 );
1194 			list[i]->GetPhysics()->UnlinkClip();
1195 			list[i]->GetPhysics()->PutToRest();
1196 		} else {
1197 #ifdef _D3XP
1198 			list[i]->GetPhysics()->SetContents( 0 );
1199 #else
1200 			list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE );
1201 #endif
1202 			list[i]->GetPhysics()->SetClipMask( CONTENTS_SOLID );
1203 			velocity = list[i]->GetPhysics()->GetAbsBounds().GetCenter() - entityCenter;
1204 			velocity.NormalizeFast();
1205 			velocity += ( i & 1 ) ? dir : -dir;
1206 			list[i]->GetPhysics()->SetLinearVelocity( velocity * 75.0f );
1207 		}
1208 #ifdef _D3XP
1209 		// Don't allow grabber to pick up temporary gibs
1210 		list[i]->noGrab = true;
1211 #endif
1212 		list[i]->GetRenderEntity()->noShadow = true;
1213 		list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1214 		list[i]->PostEventSec( &EV_Remove, 4.0f );
1215 	}
1216 }
1217 
1218 /*
1219 ============
1220 idAFEntity_Gibbable::Gib
1221 ============
1222 */
Gib(const idVec3 & dir,const char * damageDefName)1223 void idAFEntity_Gibbable::Gib( const idVec3 &dir, const char *damageDefName ) {
1224 	// only gib once
1225 	if ( gibbed ) {
1226 		return;
1227 	}
1228 
1229 #ifdef _D3XP
1230 	// Don't grab this ent after it's been gibbed (and now invisible!)
1231 	noGrab = true;
1232 #endif
1233 
1234 	const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1235 	if ( !damageDef ) {
1236 		gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1237 	}
1238 
1239 	if ( damageDef->GetBool( "gibNonSolid" ) ) {
1240 		GetAFPhysics()->SetContents( 0 );
1241 		GetAFPhysics()->SetClipMask( 0 );
1242 		GetAFPhysics()->UnlinkClip();
1243 		GetAFPhysics()->PutToRest();
1244 	} else {
1245 		GetAFPhysics()->SetContents( CONTENTS_CORPSE );
1246 		GetAFPhysics()->SetClipMask( CONTENTS_SOLID );
1247 	}
1248 
1249 	UnlinkCombat();
1250 
1251 	if ( g_bloodEffects.GetBool() ) {
1252 		if ( gameLocal.time > gameLocal.GetGibTime() ) {
1253 			gameLocal.SetGibTime( gameLocal.time + GIB_DELAY );
1254 			SpawnGibs( dir, damageDefName );
1255 			renderEntity.noShadow = true;
1256 			renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1257 			StartSound( "snd_gibbed", SND_CHANNEL_ANY, 0, false, NULL );
1258 			gibbed = true;
1259 		}
1260 	} else {
1261 		gibbed = true;
1262 	}
1263 
1264 
1265 	PostEventSec( &EV_Gibbed, 4.0f );
1266 }
1267 
1268 /*
1269 ============
1270 idAFEntity_Gibbable::Event_Gib
1271 ============
1272 */
Event_Gib(const char * damageDefName)1273 void idAFEntity_Gibbable::Event_Gib( const char *damageDefName ) {
1274 	Gib( idVec3( 0, 0, 1 ), damageDefName );
1275 }
1276 
1277 /*
1278 ===============================================================================
1279 
1280   idAFEntity_Generic
1281 
1282 ===============================================================================
1283 */
1284 
CLASS_DECLARATION(idAFEntity_Gibbable,idAFEntity_Generic)1285 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_Generic )
1286 	EVENT( EV_Activate,			idAFEntity_Generic::Event_Activate )
1287 END_CLASS
1288 
1289 /*
1290 ================
1291 idAFEntity_Generic::idAFEntity_Generic
1292 ================
1293 */
1294 idAFEntity_Generic::idAFEntity_Generic( void ) {
1295 	keepRunningPhysics = false;
1296 }
1297 
1298 /*
1299 ================
1300 idAFEntity_Generic::~idAFEntity_Generic
1301 ================
1302 */
~idAFEntity_Generic(void)1303 idAFEntity_Generic::~idAFEntity_Generic( void ) {
1304 }
1305 
1306 /*
1307 ================
1308 idAFEntity_Generic::Save
1309 ================
1310 */
Save(idSaveGame * savefile) const1311 void idAFEntity_Generic::Save( idSaveGame *savefile ) const {
1312 	savefile->WriteBool( keepRunningPhysics );
1313 }
1314 
1315 /*
1316 ================
1317 idAFEntity_Generic::Restore
1318 ================
1319 */
Restore(idRestoreGame * savefile)1320 void idAFEntity_Generic::Restore( idRestoreGame *savefile ) {
1321 	savefile->ReadBool( keepRunningPhysics );
1322 }
1323 
1324 /*
1325 ================
1326 idAFEntity_Generic::Think
1327 ================
1328 */
Think(void)1329 void idAFEntity_Generic::Think( void ) {
1330 	idAFEntity_Base::Think();
1331 
1332 	if ( keepRunningPhysics ) {
1333 		BecomeActive( TH_PHYSICS );
1334 	}
1335 }
1336 
1337 /*
1338 ================
1339 idAFEntity_Generic::Spawn
1340 ================
1341 */
Spawn(void)1342 void idAFEntity_Generic::Spawn( void ) {
1343 	if ( !LoadAF() ) {
1344 		gameLocal.Error( "Couldn't load af file on entity '%s'", name.c_str() );
1345 	}
1346 
1347 	SetCombatModel();
1348 
1349 	SetPhysics( af.GetPhysics() );
1350 
1351 	af.GetPhysics()->PutToRest();
1352 	if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1353 		af.GetPhysics()->Activate();
1354 	}
1355 
1356 	fl.takedamage = true;
1357 }
1358 
1359 /*
1360 ================
1361 idAFEntity_Generic::Event_Activate
1362 ================
1363 */
Event_Activate(idEntity * activator)1364 void idAFEntity_Generic::Event_Activate( idEntity *activator ) {
1365 	float delay;
1366 	idVec3 init_velocity, init_avelocity;
1367 
1368 	Show();
1369 
1370 	af.GetPhysics()->EnableImpact();
1371 	af.GetPhysics()->Activate();
1372 
1373 	spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1374 	spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1375 
1376 	delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1377 	if ( delay == 0.0f ) {
1378 		af.GetPhysics()->SetLinearVelocity( init_velocity );
1379 	} else {
1380 		PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1381 	}
1382 
1383 	delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1384 	if ( delay == 0.0f ) {
1385 		af.GetPhysics()->SetAngularVelocity( init_avelocity );
1386 	} else {
1387 		PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1388 	}
1389 }
1390 
1391 
1392 /*
1393 ===============================================================================
1394 
1395   idAFEntity_WithAttachedHead
1396 
1397 ===============================================================================
1398 */
1399 
CLASS_DECLARATION(idAFEntity_Gibbable,idAFEntity_WithAttachedHead)1400 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_WithAttachedHead )
1401 	EVENT( EV_Gib,				idAFEntity_WithAttachedHead::Event_Gib )
1402 	EVENT( EV_Activate,			idAFEntity_WithAttachedHead::Event_Activate )
1403 END_CLASS
1404 
1405 /*
1406 ================
1407 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead
1408 ================
1409 */
1410 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead() {
1411 	head = NULL;
1412 }
1413 
1414 /*
1415 ================
1416 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead
1417 ================
1418 */
~idAFEntity_WithAttachedHead()1419 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead() {
1420 	if ( head.GetEntity() ) {
1421 		head.GetEntity()->ClearBody();
1422 		head.GetEntity()->PostEventMS( &EV_Remove, 0 );
1423 	}
1424 }
1425 
1426 /*
1427 ================
1428 idAFEntity_WithAttachedHead::Spawn
1429 ================
1430 */
Spawn(void)1431 void idAFEntity_WithAttachedHead::Spawn( void ) {
1432 	SetupHead();
1433 
1434 	LoadAF();
1435 
1436 	SetCombatModel();
1437 
1438 	SetPhysics( af.GetPhysics() );
1439 
1440 	af.GetPhysics()->PutToRest();
1441 	if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1442 		af.GetPhysics()->Activate();
1443 	}
1444 
1445 	fl.takedamage = true;
1446 
1447 	if ( head.GetEntity() ) {
1448 		int anim = head.GetEntity()->GetAnimator()->GetAnim( "dead" );
1449 
1450 		if ( anim ) {
1451 			head.GetEntity()->GetAnimator()->SetFrame( ANIMCHANNEL_ALL, anim, 0, gameLocal.time, 0 );
1452 		}
1453 	}
1454 }
1455 
1456 /*
1457 ================
1458 idAFEntity_WithAttachedHead::Save
1459 ================
1460 */
Save(idSaveGame * savefile) const1461 void idAFEntity_WithAttachedHead::Save( idSaveGame *savefile ) const {
1462 	head.Save( savefile );
1463 }
1464 
1465 /*
1466 ================
1467 idAFEntity_WithAttachedHead::Restore
1468 ================
1469 */
Restore(idRestoreGame * savefile)1470 void idAFEntity_WithAttachedHead::Restore( idRestoreGame *savefile ) {
1471 	head.Restore( savefile );
1472 }
1473 
1474 /*
1475 ================
1476 idAFEntity_WithAttachedHead::SetupHead
1477 ================
1478 */
SetupHead(void)1479 void idAFEntity_WithAttachedHead::SetupHead( void ) {
1480 	idAFAttachment		*headEnt;
1481 	idStr				jointName;
1482 	const char			*headModel;
1483 	jointHandle_t		joint;
1484 	idVec3				origin;
1485 	idMat3				axis;
1486 
1487 	headModel = spawnArgs.GetString( "def_head", "" );
1488 	if ( headModel[ 0 ] ) {
1489 		jointName = spawnArgs.GetString( "head_joint" );
1490 		joint = animator.GetJointHandle( jointName );
1491 		if ( joint == INVALID_JOINT ) {
1492 			gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
1493 		}
1494 
1495 		headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, NULL ) );
1496 		headEnt->SetName( va( "%s_head", name.c_str() ) );
1497 		headEnt->SetBody( this, headModel, joint );
1498 		headEnt->SetCombatModel();
1499 		head = headEnt;
1500 
1501 #ifdef _D3XP
1502 		idStr xSkin;
1503 		if ( spawnArgs.GetString( "skin_head_xray", "", xSkin ) ) {
1504 			headEnt->xraySkin = declManager->FindSkin( xSkin.c_str() );
1505 			headEnt->UpdateModel();
1506 		}
1507 #endif
1508 		animator.GetJointTransform( joint, gameLocal.time, origin, axis );
1509 		origin = renderEntity.origin + origin * renderEntity.axis;
1510 		headEnt->SetOrigin( origin );
1511 		headEnt->SetAxis( renderEntity.axis );
1512 		headEnt->BindToJoint( this, joint, true );
1513 	}
1514 }
1515 
1516 /*
1517 ================
1518 idAFEntity_WithAttachedHead::Think
1519 ================
1520 */
Think(void)1521 void idAFEntity_WithAttachedHead::Think( void ) {
1522 	idAFEntity_Base::Think();
1523 }
1524 
1525 /*
1526 ================
1527 idAFEntity_WithAttachedHead::LinkCombat
1528 ================
1529 */
LinkCombat(void)1530 void idAFEntity_WithAttachedHead::LinkCombat( void ) {
1531 	idAFAttachment *headEnt;
1532 
1533 	if ( fl.hidden ) {
1534 		return;
1535 	}
1536 
1537 	if ( combatModel ) {
1538 		combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1539 	}
1540 	headEnt = head.GetEntity();
1541 	if ( headEnt ) {
1542 		headEnt->LinkCombat();
1543 	}
1544 }
1545 
1546 /*
1547 ================
1548 idAFEntity_WithAttachedHead::UnlinkCombat
1549 ================
1550 */
UnlinkCombat(void)1551 void idAFEntity_WithAttachedHead::UnlinkCombat( void ) {
1552 	idAFAttachment *headEnt;
1553 
1554 	if ( combatModel ) {
1555 		combatModel->Unlink();
1556 	}
1557 	headEnt = head.GetEntity();
1558 	if ( headEnt ) {
1559 		headEnt->UnlinkCombat();
1560 	}
1561 }
1562 
1563 /*
1564 ================
1565 idAFEntity_WithAttachedHead::Hide
1566 ================
1567 */
Hide(void)1568 void idAFEntity_WithAttachedHead::Hide( void ) {
1569 	idAFEntity_Base::Hide();
1570 	if ( head.GetEntity() ) {
1571 		head.GetEntity()->Hide();
1572 	}
1573 	UnlinkCombat();
1574 }
1575 
1576 /*
1577 ================
1578 idAFEntity_WithAttachedHead::Show
1579 ================
1580 */
Show(void)1581 void idAFEntity_WithAttachedHead::Show( void ) {
1582 	idAFEntity_Base::Show();
1583 	if ( head.GetEntity() ) {
1584 		head.GetEntity()->Show();
1585 	}
1586 	LinkCombat();
1587 }
1588 
1589 /*
1590 ================
1591 idAFEntity_WithAttachedHead::ProjectOverlay
1592 ================
1593 */
ProjectOverlay(const idVec3 & origin,const idVec3 & dir,float size,const char * material)1594 void idAFEntity_WithAttachedHead::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1595 
1596 	idEntity::ProjectOverlay( origin, dir, size, material );
1597 
1598 	if ( head.GetEntity() ) {
1599 		head.GetEntity()->ProjectOverlay( origin, dir, size, material );
1600 	}
1601 }
1602 
1603 /*
1604 ============
1605 idAFEntity_WithAttachedHead::Gib
1606 ============
1607 */
Gib(const idVec3 & dir,const char * damageDefName)1608 void idAFEntity_WithAttachedHead::Gib( const idVec3 &dir, const char *damageDefName ) {
1609 	// only gib once
1610 	if ( gibbed ) {
1611 		return;
1612 	}
1613 	idAFEntity_Gibbable::Gib( dir, damageDefName );
1614 	if ( head.GetEntity() ) {
1615 		head.GetEntity()->Hide();
1616 	}
1617 }
1618 
1619 /*
1620 ============
1621 idAFEntity_WithAttachedHead::Event_Gib
1622 ============
1623 */
Event_Gib(const char * damageDefName)1624 void idAFEntity_WithAttachedHead::Event_Gib( const char *damageDefName ) {
1625 	Gib( idVec3( 0, 0, 1 ), damageDefName );
1626 }
1627 
1628 /*
1629 ================
1630 idAFEntity_WithAttachedHead::Event_Activate
1631 ================
1632 */
Event_Activate(idEntity * activator)1633 void idAFEntity_WithAttachedHead::Event_Activate( idEntity *activator ) {
1634 	float delay;
1635 	idVec3 init_velocity, init_avelocity;
1636 
1637 	Show();
1638 
1639 	af.GetPhysics()->EnableImpact();
1640 	af.GetPhysics()->Activate();
1641 
1642 	spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1643 	spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1644 
1645 	delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1646 	if ( delay == 0.0f ) {
1647 		af.GetPhysics()->SetLinearVelocity( init_velocity );
1648 	} else {
1649 		PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1650 	}
1651 
1652 	delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1653 	if ( delay == 0.0f ) {
1654 		af.GetPhysics()->SetAngularVelocity( init_avelocity );
1655 	} else {
1656 		PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1657 	}
1658 }
1659 
1660 
1661 /*
1662 ===============================================================================
1663 
1664   idAFEntity_Vehicle
1665 
1666 ===============================================================================
1667 */
1668 
CLASS_DECLARATION(idAFEntity_Base,idAFEntity_Vehicle)1669 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Vehicle )
1670 END_CLASS
1671 
1672 /*
1673 ================
1674 idAFEntity_Vehicle::idAFEntity_Vehicle
1675 ================
1676 */
1677 idAFEntity_Vehicle::idAFEntity_Vehicle( void ) {
1678 	player				= NULL;
1679 	eyesJoint			= INVALID_JOINT;
1680 	steeringWheelJoint	= INVALID_JOINT;
1681 	wheelRadius			= 0.0f;
1682 	steerAngle			= 0.0f;
1683 	steerSpeed			= 0.0f;
1684 	dustSmoke			= NULL;
1685 }
1686 
1687 /*
1688 ================
1689 idAFEntity_Vehicle::Spawn
1690 ================
1691 */
Spawn(void)1692 void idAFEntity_Vehicle::Spawn( void ) {
1693 	const char *eyesJointName = spawnArgs.GetString( "eyesJoint", "eyes" );
1694 	const char *steeringWheelJointName = spawnArgs.GetString( "steeringWheelJoint", "steeringWheel" );
1695 
1696 	LoadAF();
1697 
1698 	SetCombatModel();
1699 
1700 	SetPhysics( af.GetPhysics() );
1701 
1702 	fl.takedamage = true;
1703 
1704 	if ( !eyesJointName[0] ) {
1705 		gameLocal.Error( "idAFEntity_Vehicle '%s' no eyes joint specified", name.c_str() );
1706 	}
1707 	eyesJoint = animator.GetJointHandle( eyesJointName );
1708 	if ( !steeringWheelJointName[0] ) {
1709 		gameLocal.Error( "idAFEntity_Vehicle '%s' no steering wheel joint specified", name.c_str() );
1710 	}
1711 	steeringWheelJoint = animator.GetJointHandle( steeringWheelJointName );
1712 
1713 	spawnArgs.GetFloat( "wheelRadius", "20", wheelRadius );
1714 	spawnArgs.GetFloat( "steerSpeed", "5", steerSpeed );
1715 
1716 	player = NULL;
1717 	steerAngle = 0.0f;
1718 
1719 	const char *smokeName = spawnArgs.GetString( "smoke_vehicle_dust", "muzzlesmoke" );
1720 	if ( *smokeName != '\0' ) {
1721 		dustSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1722 	}
1723 }
1724 
1725 /*
1726 ================
1727 idAFEntity_Vehicle::Use
1728 ================
1729 */
Use(idPlayer * other)1730 void idAFEntity_Vehicle::Use( idPlayer *other ) {
1731 	idVec3 origin;
1732 	idMat3 axis;
1733 
1734 	if ( player ) {
1735 		if ( player == other ) {
1736 			other->Unbind();
1737 			player = NULL;
1738 
1739 			af.GetPhysics()->SetComeToRest( true );
1740 		}
1741 	}
1742 	else {
1743 		player = other;
1744 		animator.GetJointTransform( eyesJoint, gameLocal.time, origin, axis );
1745 		origin = renderEntity.origin + origin * renderEntity.axis;
1746 		player->GetPhysics()->SetOrigin( origin );
1747 		player->BindToBody( this, 0, true );
1748 
1749 		af.GetPhysics()->SetComeToRest( false );
1750 		af.GetPhysics()->Activate();
1751 	}
1752 }
1753 
1754 /*
1755 ================
1756 idAFEntity_Vehicle::GetSteerAngle
1757 ================
1758 */
GetSteerAngle(void)1759 float idAFEntity_Vehicle::GetSteerAngle( void ) {
1760 	float idealSteerAngle, angleDelta;
1761 
1762 	idealSteerAngle = player->usercmd.rightmove * ( 30.0f / 128.0f );
1763 	angleDelta = idealSteerAngle - steerAngle;
1764 
1765 	if ( angleDelta > steerSpeed ) {
1766 		steerAngle += steerSpeed;
1767 	} else if ( angleDelta < -steerSpeed ) {
1768 		steerAngle -= steerSpeed;
1769 	} else {
1770 		steerAngle = idealSteerAngle;
1771 	}
1772 
1773 	return steerAngle;
1774 }
1775 
1776 
1777 /*
1778 ===============================================================================
1779 
1780   idAFEntity_VehicleSimple
1781 
1782 ===============================================================================
1783 */
1784 
CLASS_DECLARATION(idAFEntity_Vehicle,idAFEntity_VehicleSimple)1785 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSimple )
1786 END_CLASS
1787 
1788 /*
1789 ================
1790 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple
1791 ================
1792 */
1793 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple( void ) {
1794 	int i;
1795 	for ( i = 0; i < 4; i++ ) {
1796 		suspension[i] = NULL;
1797 	}
1798 }
1799 
1800 /*
1801 ================
1802 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple
1803 ================
1804 */
~idAFEntity_VehicleSimple(void)1805 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple( void ) {
1806 	delete wheelModel;
1807 	wheelModel = NULL;
1808 }
1809 
1810 /*
1811 ================
1812 idAFEntity_VehicleSimple::Spawn
1813 ================
1814 */
Spawn(void)1815 void idAFEntity_VehicleSimple::Spawn( void ) {
1816 	static const char *wheelJointKeys[] = {
1817 		"wheelJointFrontLeft",
1818 		"wheelJointFrontRight",
1819 		"wheelJointRearLeft",
1820 		"wheelJointRearRight"
1821 	};
1822 	static idVec3 wheelPoly[4] = { idVec3( 2, 2, 0 ), idVec3( 2, -2, 0 ), idVec3( -2, -2, 0 ), idVec3( -2, 2, 0 ) };
1823 
1824 	int i;
1825 	idVec3 origin;
1826 	idMat3 axis;
1827 	idTraceModel trm;
1828 
1829 	trm.SetupPolygon( wheelPoly, 4 );
1830 	trm.Translate( idVec3( 0, 0, -wheelRadius ) );
1831 	wheelModel = new idClipModel( trm );
1832 
1833 	for ( i = 0; i < 4; i++ ) {
1834 		const char *wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
1835 		if ( !wheelJointName[0] ) {
1836 			gameLocal.Error( "idAFEntity_VehicleSimple '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
1837 		}
1838 		wheelJoints[i] = animator.GetJointHandle( wheelJointName );
1839 		if ( wheelJoints[i] == INVALID_JOINT ) {
1840 			gameLocal.Error( "idAFEntity_VehicleSimple '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
1841 		}
1842 
1843 		GetAnimator()->GetJointTransform( wheelJoints[i], 0, origin, axis );
1844 		origin = renderEntity.origin + origin * renderEntity.axis;
1845 
1846 		suspension[i] = new idAFConstraint_Suspension();
1847 		suspension[i]->Setup( va( "suspension%d", i ), af.GetPhysics()->GetBody( 0 ), origin, af.GetPhysics()->GetAxis( 0 ), wheelModel );
1848 		suspension[i]->SetSuspension(	g_vehicleSuspensionUp.GetFloat(),
1849 										g_vehicleSuspensionDown.GetFloat(),
1850 										g_vehicleSuspensionKCompress.GetFloat(),
1851 										g_vehicleSuspensionDamping.GetFloat(),
1852 										g_vehicleTireFriction.GetFloat() );
1853 
1854 		af.GetPhysics()->AddConstraint( suspension[i] );
1855 	}
1856 
1857 	memset( wheelAngles, 0, sizeof( wheelAngles ) );
1858 	BecomeActive( TH_THINK );
1859 }
1860 
1861 /*
1862 ================
1863 idAFEntity_VehicleSimple::Think
1864 ================
1865 */
Think(void)1866 void idAFEntity_VehicleSimple::Think( void ) {
1867 	int i;
1868 	float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
1869 	idVec3 origin;
1870 	idMat3 axis;
1871 	idRotation wheelRotation, steerRotation;
1872 
1873 	if ( thinkFlags & TH_THINK ) {
1874 
1875 		if ( player ) {
1876 			// capture the input from a player
1877 			velocity = g_vehicleVelocity.GetFloat();
1878 			if ( player->usercmd.forwardmove < 0 ) {
1879 				velocity = -velocity;
1880 			}
1881 			force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
1882 			steerAngle = GetSteerAngle();
1883 		}
1884 
1885 		// update the wheel motor force and steering
1886 		for ( i = 0; i < 2; i++ ) {
1887 
1888 			// front wheel drive
1889 			if ( velocity != 0.0f ) {
1890 				suspension[i]->EnableMotor( true );
1891 			} else {
1892 				suspension[i]->EnableMotor( false );
1893 			}
1894 			suspension[i]->SetMotorVelocity( velocity );
1895 			suspension[i]->SetMotorForce( force );
1896 
1897 			// update the wheel steering
1898 			suspension[i]->SetSteerAngle( steerAngle );
1899 		}
1900 
1901 		// adjust wheel velocity for better steering because there are no differentials between the wheels
1902 		if ( steerAngle < 0.0f ) {
1903 			suspension[0]->SetMotorVelocity( velocity * 0.5f );
1904 		} else if ( steerAngle > 0.0f ) {
1905 			suspension[1]->SetMotorVelocity( velocity * 0.5f );
1906 		}
1907 
1908 		// update suspension with latest cvar settings
1909 		for ( i = 0; i < 4; i++ ) {
1910 			suspension[i]->SetSuspension(	g_vehicleSuspensionUp.GetFloat(),
1911 											g_vehicleSuspensionDown.GetFloat(),
1912 											g_vehicleSuspensionKCompress.GetFloat(),
1913 											g_vehicleSuspensionDamping.GetFloat(),
1914 											g_vehicleTireFriction.GetFloat() );
1915 		}
1916 
1917 		// run the physics
1918 		RunPhysics();
1919 
1920 		// move and rotate the wheels visually
1921 		for ( i = 0; i < 4; i++ ) {
1922 			idAFBody *body = af.GetPhysics()->GetBody( 0 );
1923 
1924 			origin = suspension[i]->GetWheelOrigin();
1925 			velocity = body->GetPointVelocity( origin ) * body->GetWorldAxis()[0];
1926 			wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
1927 
1928 			// additional rotation about the wheel axis
1929 			wheelRotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
1930 			wheelRotation.SetVec( 0, -1, 0 );
1931 
1932 			if ( i < 2 ) {
1933 				// rotate the wheel for steering
1934 				steerRotation.SetAngle( steerAngle );
1935 				steerRotation.SetVec( 0, 0, 1 );
1936 				// set wheel rotation
1937 				animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() * steerRotation.ToMat3() );
1938 			} else {
1939 				// set wheel rotation
1940 				animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() );
1941 			}
1942 
1943 			// set wheel position for suspension
1944 			origin = ( origin - renderEntity.origin ) * renderEntity.axis.Transpose();
1945 			GetAnimator()->SetJointPos( wheelJoints[i], JOINTMOD_WORLD_OVERRIDE, origin );
1946 		}
1947 /*
1948 		// spawn dust particle effects
1949 		if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
1950 			int numContacts;
1951 			idAFConstraint_Contact *contacts[2];
1952 			for ( i = 0; i < 4; i++ ) {
1953 				numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
1954 				for ( int j = 0; j < numContacts; j++ ) {
1955 					gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
1956 				}
1957 			}
1958 		}
1959 */
1960 	}
1961 
1962 	UpdateAnimation();
1963 	if ( thinkFlags & TH_UPDATEVISUALS ) {
1964 		Present();
1965 		LinkCombat();
1966 	}
1967 }
1968 
1969 
1970 /*
1971 ===============================================================================
1972 
1973   idAFEntity_VehicleFourWheels
1974 
1975 ===============================================================================
1976 */
1977 
CLASS_DECLARATION(idAFEntity_Vehicle,idAFEntity_VehicleFourWheels)1978 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleFourWheels )
1979 END_CLASS
1980 
1981 
1982 /*
1983 ================
1984 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels
1985 ================
1986 */
1987 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels( void ) {
1988 	int i;
1989 
1990 	for ( i = 0; i < 4; i++ ) {
1991 		wheels[i]		= NULL;
1992 		wheelJoints[i]	= INVALID_JOINT;
1993 		wheelAngles[i]	= 0.0f;
1994 	}
1995 	steering[0]			= NULL;
1996 	steering[1]			= NULL;
1997 }
1998 
1999 /*
2000 ================
2001 idAFEntity_VehicleFourWheels::Spawn
2002 ================
2003 */
Spawn(void)2004 void idAFEntity_VehicleFourWheels::Spawn( void ) {
2005 	int i;
2006 	static const char *wheelBodyKeys[] = {
2007 		"wheelBodyFrontLeft",
2008 		"wheelBodyFrontRight",
2009 		"wheelBodyRearLeft",
2010 		"wheelBodyRearRight"
2011 	};
2012 	static const char *wheelJointKeys[] = {
2013 		"wheelJointFrontLeft",
2014 		"wheelJointFrontRight",
2015 		"wheelJointRearLeft",
2016 		"wheelJointRearRight"
2017 	};
2018 	static const char *steeringHingeKeys[] = {
2019 		"steeringHingeFrontLeft",
2020 		"steeringHingeFrontRight",
2021 	};
2022 
2023 	const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2024 
2025 	for ( i = 0; i < 4; i++ ) {
2026 		wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2027 		if ( !wheelBodyName[0] ) {
2028 			gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2029 		}
2030 		wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2031 		if ( !wheels[i] ) {
2032 			gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2033 		}
2034 		wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2035 		if ( !wheelJointName[0] ) {
2036 			gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2037 		}
2038 		wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2039 		if ( wheelJoints[i] == INVALID_JOINT ) {
2040 			gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2041 		}
2042 	}
2043 
2044 	for ( i = 0; i < 2; i++ ) {
2045 		steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2046 		if ( !steeringHingeName[0] ) {
2047 			gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2048 		}
2049 		steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2050 		if ( !steering[i] ) {
2051 			gameLocal.Error( "idAFEntity_VehicleFourWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2052 		}
2053 	}
2054 
2055 	memset( wheelAngles, 0, sizeof( wheelAngles ) );
2056 	BecomeActive( TH_THINK );
2057 }
2058 
2059 /*
2060 ================
2061 idAFEntity_VehicleFourWheels::Think
2062 ================
2063 */
Think(void)2064 void idAFEntity_VehicleFourWheels::Think( void ) {
2065 	int i;
2066 	float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2067 	idVec3 origin;
2068 	idMat3 axis;
2069 	idRotation rotation;
2070 
2071 	if ( thinkFlags & TH_THINK ) {
2072 
2073 		if ( player ) {
2074 			// capture the input from a player
2075 			velocity = g_vehicleVelocity.GetFloat();
2076 			if ( player->usercmd.forwardmove < 0 ) {
2077 				velocity = -velocity;
2078 			}
2079 			force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2080 			steerAngle = GetSteerAngle();
2081 		}
2082 
2083 		// update the wheel motor force
2084 		for ( i = 0; i < 2; i++ ) {
2085 			wheels[2+i]->SetContactMotorVelocity( velocity );
2086 			wheels[2+i]->SetContactMotorForce( force );
2087 		}
2088 
2089 		// adjust wheel velocity for better steering because there are no differentials between the wheels
2090 		if ( steerAngle < 0.0f ) {
2091 			wheels[2]->SetContactMotorVelocity( velocity * 0.5f );
2092 		}
2093 		else if ( steerAngle > 0.0f ) {
2094 			wheels[3]->SetContactMotorVelocity( velocity * 0.5f );
2095 		}
2096 
2097 		// update the wheel steering
2098 		steering[0]->SetSteerAngle( steerAngle );
2099 		steering[1]->SetSteerAngle( steerAngle );
2100 		for ( i = 0; i < 2; i++ ) {
2101 			steering[i]->SetSteerSpeed( 3.0f );
2102 		}
2103 
2104 		// update the steering wheel
2105 		animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2106 		rotation.SetVec( axis[2] );
2107 		rotation.SetAngle( -steerAngle );
2108 		animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2109 
2110 		// run the physics
2111 		RunPhysics();
2112 
2113 		// rotate the wheels visually
2114 		for ( i = 0; i < 4; i++ ) {
2115 			if ( force == 0.0f ) {
2116 				velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2117 			}
2118 			wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2119 			// give the wheel joint an additional rotation about the wheel axis
2120 			rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2121 			axis = af.GetPhysics()->GetAxis( 0 );
2122 			rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2123 			animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2124 		}
2125 
2126 		// spawn dust particle effects
2127 		if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2128 			int numContacts;
2129 			idAFConstraint_Contact *contacts[2];
2130 			for ( i = 0; i < 4; i++ ) {
2131 				numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2132 				for ( int j = 0; j < numContacts; j++ ) {
2133 					gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
2134 				}
2135 			}
2136 		}
2137 	}
2138 
2139 	UpdateAnimation();
2140 	if ( thinkFlags & TH_UPDATEVISUALS ) {
2141 		Present();
2142 		LinkCombat();
2143 	}
2144 }
2145 
2146 
2147 /*
2148 ===============================================================================
2149 
2150   idAFEntity_VehicleSixWheels
2151 
2152 ===============================================================================
2153 */
2154 
CLASS_DECLARATION(idAFEntity_Vehicle,idAFEntity_VehicleSixWheels)2155 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSixWheels )
2156 END_CLASS
2157 
2158 	/*
2159 ================
2160 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels
2161 ================
2162 */
2163 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels( void ) {
2164 	int i;
2165 
2166 	for ( i = 0; i < 6; i++ ) {
2167 		wheels[i]		= NULL;
2168 		wheelJoints[i]	= INVALID_JOINT;
2169 		wheelAngles[i]	= 0.0f;
2170 	}
2171 	steering[0]			= NULL;
2172 	steering[1]			= NULL;
2173 	steering[2]			= NULL;
2174 	steering[3]			= NULL;
2175 }
2176 
2177 /*
2178 ================
2179 idAFEntity_VehicleSixWheels::Spawn
2180 ================
2181 */
Spawn(void)2182 void idAFEntity_VehicleSixWheels::Spawn( void ) {
2183 	int i;
2184 	static const char *wheelBodyKeys[] = {
2185 		"wheelBodyFrontLeft",
2186 		"wheelBodyFrontRight",
2187 		"wheelBodyMiddleLeft",
2188 		"wheelBodyMiddleRight",
2189 		"wheelBodyRearLeft",
2190 		"wheelBodyRearRight"
2191 	};
2192 	static const char *wheelJointKeys[] = {
2193 		"wheelJointFrontLeft",
2194 		"wheelJointFrontRight",
2195 		"wheelJointMiddleLeft",
2196 		"wheelJointMiddleRight",
2197 		"wheelJointRearLeft",
2198 		"wheelJointRearRight"
2199 	};
2200 	static const char *steeringHingeKeys[] = {
2201 		"steeringHingeFrontLeft",
2202 		"steeringHingeFrontRight",
2203 		"steeringHingeRearLeft",
2204 		"steeringHingeRearRight"
2205 	};
2206 
2207 	const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2208 
2209 	for ( i = 0; i < 6; i++ ) {
2210 		wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2211 		if ( !wheelBodyName[0] ) {
2212 			gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2213 		}
2214 		wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2215 		if ( !wheels[i] ) {
2216 			gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2217 		}
2218 		wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2219 		if ( !wheelJointName[0] ) {
2220 			gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2221 		}
2222 		wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2223 		if ( wheelJoints[i] == INVALID_JOINT ) {
2224 			gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2225 		}
2226 	}
2227 
2228 	for ( i = 0; i < 4; i++ ) {
2229 		steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2230 		if ( !steeringHingeName[0] ) {
2231 			gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2232 		}
2233 		steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2234 		if ( !steering[i] ) {
2235 			gameLocal.Error( "idAFEntity_VehicleSixWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2236 		}
2237 	}
2238 
2239 	memset( wheelAngles, 0, sizeof( wheelAngles ) );
2240 	BecomeActive( TH_THINK );
2241 }
2242 
2243 /*
2244 ================
2245 idAFEntity_VehicleSixWheels::Think
2246 ================
2247 */
Think(void)2248 void idAFEntity_VehicleSixWheels::Think( void ) {
2249 	int i;
2250 #ifndef _D3XP
2251 	float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2252 #endif
2253 	idVec3 origin;
2254 	idMat3 axis;
2255 	idRotation rotation;
2256 
2257 	if ( thinkFlags & TH_THINK ) {
2258 
2259 		if ( player ) {
2260 			// capture the input from a player
2261 			velocity = g_vehicleVelocity.GetFloat();
2262 			if ( player->usercmd.forwardmove < 0 ) {
2263 				velocity = -velocity;
2264 			}
2265 			force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2266 			steerAngle = GetSteerAngle();
2267 		}
2268 
2269 		// update the wheel motor force
2270 		for ( i = 0; i < 6; i++ ) {
2271 			wheels[i]->SetContactMotorVelocity( velocity );
2272 			wheels[i]->SetContactMotorForce( force );
2273 		}
2274 
2275 		// adjust wheel velocity for better steering because there are no differentials between the wheels
2276 		if ( steerAngle < 0.0f ) {
2277 			for ( i = 0; i < 3; i++ ) {
2278 				wheels[(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2279 			}
2280 		}
2281 		else if ( steerAngle > 0.0f ) {
2282 			for ( i = 0; i < 3; i++ ) {
2283 				wheels[1+(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2284 			}
2285 		}
2286 
2287 		// update the wheel steering
2288 		steering[0]->SetSteerAngle( steerAngle );
2289 		steering[1]->SetSteerAngle( steerAngle );
2290 		steering[2]->SetSteerAngle( -steerAngle );
2291 		steering[3]->SetSteerAngle( -steerAngle );
2292 		for ( i = 0; i < 4; i++ ) {
2293 			steering[i]->SetSteerSpeed( 3.0f );
2294 		}
2295 
2296 		// update the steering wheel
2297 		animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2298 		rotation.SetVec( axis[2] );
2299 		rotation.SetAngle( -steerAngle );
2300 		animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2301 
2302 		// run the physics
2303 		RunPhysics();
2304 
2305 		// rotate the wheels visually
2306 		for ( i = 0; i < 6; i++ ) {
2307 			if ( force == 0.0f ) {
2308 				velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2309 			}
2310 			wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2311 			// give the wheel joint an additional rotation about the wheel axis
2312 			rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2313 			axis = af.GetPhysics()->GetAxis( 0 );
2314 			rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2315 			animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2316 		}
2317 
2318 		// spawn dust particle effects
2319 		if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2320 			int numContacts;
2321 			idAFConstraint_Contact *contacts[2];
2322 			for ( i = 0; i < 6; i++ ) {
2323 				numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2324 				for ( int j = 0; j < numContacts; j++ ) {
2325 					gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
2326 				}
2327 			}
2328 		}
2329 	}
2330 
2331 	UpdateAnimation();
2332 	if ( thinkFlags & TH_UPDATEVISUALS ) {
2333 		Present();
2334 		LinkCombat();
2335 	}
2336 }
2337 
2338 #ifdef _D3XP
2339 /*
2340 ===============================================================================
2341 
2342 idAFEntity_VehicleAutomated
2343 
2344 ===============================================================================
2345 */
2346 const idEventDef EV_Vehicle_setVelocity( "setVelocity", "f" );
2347 const idEventDef EV_Vehicle_setTorque( "setTorque", "f" );
2348 const idEventDef EV_Vehicle_setSteeringSpeed( "setSteeringSpeed", "f" );
2349 const idEventDef EV_Vehicle_setWaypoint( "setWaypoint", "e" );
2350 
CLASS_DECLARATION(idAFEntity_VehicleSixWheels,idAFEntity_VehicleAutomated)2351 CLASS_DECLARATION( idAFEntity_VehicleSixWheels, idAFEntity_VehicleAutomated )
2352 EVENT( EV_PostSpawn,				idAFEntity_VehicleAutomated::PostSpawn )
2353 EVENT( EV_Vehicle_setVelocity,		idAFEntity_VehicleAutomated::Event_SetVelocity )
2354 EVENT( EV_Vehicle_setTorque,		idAFEntity_VehicleAutomated::Event_SetTorque )
2355 EVENT( EV_Vehicle_setSteeringSpeed,	idAFEntity_VehicleAutomated::Event_SetSteeringSpeed )
2356 EVENT( EV_Vehicle_setWaypoint,		idAFEntity_VehicleAutomated::Event_SetWayPoint )
2357 END_CLASS
2358 
2359 /*
2360 ================
2361 idAFEntity_VehicleAutomated::Spawn
2362 ================
2363 */
2364 void idAFEntity_VehicleAutomated::Spawn( void ) {
2365 
2366 	velocity = force = steerAngle = 0.f;
2367 	currentSteering = steeringSpeed = 0.f;
2368 	originHeight = 0.f;
2369 	waypoint = NULL;
2370 
2371 	spawnArgs.GetFloat( "velocity", "150", velocity );
2372 	spawnArgs.GetFloat( "torque", "200000", force );
2373 	spawnArgs.GetFloat( "steeringSpeed", "1", steeringSpeed );
2374 	spawnArgs.GetFloat( "originHeight", "0", originHeight );
2375 
2376 	PostEventMS( &EV_PostSpawn, 0 );
2377 }
2378 
2379 /*
2380 ================
2381 idAFEntity_VehicleAutomated::PostSpawn
2382 ================
2383 */
PostSpawn(void)2384 void idAFEntity_VehicleAutomated::PostSpawn( void ) {
2385 
2386 	if ( targets.Num() ) {
2387 		waypoint = targets[0].GetEntity();
2388 	}
2389 }
2390 
2391 /*
2392 ================
2393 idAFEntity_VehicleAutomated::Event_SetVelocity
2394 ================
2395 */
Event_SetVelocity(float _velocity)2396 void idAFEntity_VehicleAutomated::Event_SetVelocity( float _velocity ) {
2397 	velocity = _velocity;
2398 }
2399 
2400 /*
2401 ================
2402 idAFEntity_VehicleAutomated::Event_SetTorque
2403 ================
2404 */
Event_SetTorque(float _torque)2405 void idAFEntity_VehicleAutomated::Event_SetTorque( float _torque ) {
2406 	force = _torque;
2407 }
2408 
2409 /*
2410 ================
2411 idAFEntity_VehicleAutomated::Event_SetSteeringSpeed
2412 ================
2413 */
Event_SetSteeringSpeed(float _steeringSpeed)2414 void idAFEntity_VehicleAutomated::Event_SetSteeringSpeed( float _steeringSpeed ) {
2415 	steeringSpeed = _steeringSpeed;
2416 }
2417 
2418 /*
2419 ================
2420 idAFEntity_VehicleAutomated::Event_SetWayPoint
2421 ================
2422 */
Event_SetWayPoint(idEntity * _waypoint)2423 void idAFEntity_VehicleAutomated::Event_SetWayPoint( idEntity *_waypoint ) {
2424 	waypoint = _waypoint;
2425 }
2426 
2427 /*
2428 ================
2429 idAFEntity_VehicleAutomated::Think
2430 ================
2431 */
2432 #define	HIT_WAYPOINT_THRESHOLD	80.f
2433 
Think(void)2434 void idAFEntity_VehicleAutomated::Think( void ) {
2435 
2436 	// If we don't have a waypoint, coast to a stop
2437 	if ( !waypoint ) {
2438 		velocity = force = steerAngle = 0.f;
2439 		idAFEntity_VehicleSixWheels::Think();
2440 		return;
2441 	}
2442 
2443 	idVec3 waypoint_origin, vehicle_origin;
2444 	idVec3 travel_vector;
2445 	float distance_from_waypoint;
2446 
2447 	// Set up the vector from the vehicle origin, to the waypoint
2448 	vehicle_origin = GetPhysics()->GetOrigin();
2449 	vehicle_origin.z -= originHeight;
2450 
2451 	waypoint_origin = waypoint->GetPhysics()->GetOrigin();
2452 
2453 	travel_vector = waypoint_origin - vehicle_origin;
2454 	distance_from_waypoint = travel_vector.Length();
2455 
2456 	// Check if we've hit the waypoint (within a certain threshold)
2457 	if ( distance_from_waypoint < HIT_WAYPOINT_THRESHOLD ) {
2458 		idStr				callfunc;
2459 		const function_t	*func;
2460 		idThread			*thread;
2461 
2462 		// Waypoints can call script functions
2463 		waypoint->spawnArgs.GetString( "call", "", callfunc );
2464 		if ( callfunc.Length() ) {
2465 			func = gameLocal.program.FindFunction( callfunc );
2466 			if ( func != NULL ) {
2467 				thread = new idThread( func );
2468 				thread->DelayedStart( 0 );
2469 			}
2470 		}
2471 
2472 		// Get next waypoint
2473 		if ( waypoint->targets.Num() ) {
2474 			waypoint = waypoint->targets[0].GetEntity();
2475 		} else {
2476 			waypoint = NULL;
2477 		}
2478 
2479 		// We are switching waypoints, adjust steering next frame
2480 		idAFEntity_VehicleSixWheels::Think();
2481 		return;
2482 	}
2483 
2484 	idAngles vehicle_angles, travel_angles;
2485 
2486 	// Get the angles we need to steer towards
2487 	travel_angles = travel_vector.ToAngles().Normalize360();
2488 	vehicle_angles = this->GetPhysics()->GetAxis().ToAngles().Normalize360();
2489 
2490 	float	delta_yaw;
2491 
2492 	// Get the shortest steering angle towards the travel angles
2493 	delta_yaw = vehicle_angles.yaw - travel_angles.yaw;
2494 	if ( idMath::Fabs( delta_yaw ) > 180.f ) {
2495 		if ( delta_yaw > 0 ) {
2496 			delta_yaw = delta_yaw - 360;
2497 		} else {
2498 			delta_yaw = delta_yaw + 360;
2499 		}
2500 	}
2501 
2502 	// Maximum steering angle is 35 degrees
2503 	delta_yaw = idMath::ClampFloat( -35.f, 35.f, delta_yaw );
2504 
2505 	idealSteering = delta_yaw;
2506 
2507 	// Adjust steering incrementally so it doesn't snap to the ideal angle
2508 	if ( idMath::Fabs( (idealSteering - currentSteering) ) > steeringSpeed ) {
2509 		if ( idealSteering > currentSteering ) {
2510 			currentSteering += steeringSpeed;
2511 		} else {
2512 			currentSteering -= steeringSpeed;
2513 		}
2514 	} else {
2515 		currentSteering = idealSteering;
2516 	}
2517 
2518 	// DEBUG
2519 	if ( g_vehicleDebug.GetBool() ) {
2520 		gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), vehicle_origin );
2521 		gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), waypoint_origin );
2522 		gameRenderWorld->DrawText( waypoint->name.c_str(), waypoint_origin + idVec3(0,0,16), 0.25f, colorYellow, gameLocal.GetLocalPlayer()->viewAxis );
2523 		gameRenderWorld->DebugArrow( colorWhite, vehicle_origin, waypoint_origin, 12.f );
2524 	}
2525 
2526 	// Set the final steerAngle for the vehicle
2527 	steerAngle = currentSteering;
2528 
2529 	idAFEntity_VehicleSixWheels::Think();
2530 }
2531 #endif
2532 
2533 /*
2534 ===============================================================================
2535 
2536   idAFEntity_SteamPipe
2537 
2538 ===============================================================================
2539 */
2540 
CLASS_DECLARATION(idAFEntity_Base,idAFEntity_SteamPipe)2541 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_SteamPipe )
2542 END_CLASS
2543 
2544 
2545 /*
2546 ================
2547 idAFEntity_SteamPipe::idAFEntity_SteamPipe
2548 ================
2549 */
2550 idAFEntity_SteamPipe::idAFEntity_SteamPipe( void ) {
2551 	steamBody			= 0;
2552 	steamForce			= 0.0f;
2553 	steamUpForce		= 0.0f;
2554 	steamModelDefHandle	= -1;
2555 	memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2556 }
2557 
2558 /*
2559 ================
2560 idAFEntity_SteamPipe::~idAFEntity_SteamPipe
2561 ================
2562 */
~idAFEntity_SteamPipe(void)2563 idAFEntity_SteamPipe::~idAFEntity_SteamPipe( void ) {
2564 	if ( steamModelDefHandle >= 0 ){
2565 		gameRenderWorld->FreeEntityDef( steamModelDefHandle );
2566 	}
2567 }
2568 
2569 /*
2570 ================
2571 idAFEntity_SteamPipe::Save
2572 ================
2573 */
Save(idSaveGame * savefile) const2574 void idAFEntity_SteamPipe::Save( idSaveGame *savefile ) const {
2575 }
2576 
2577 /*
2578 ================
2579 idAFEntity_SteamPipe::Restore
2580 ================
2581 */
Restore(idRestoreGame * savefile)2582 void idAFEntity_SteamPipe::Restore( idRestoreGame *savefile ) {
2583 	Spawn();
2584 }
2585 
2586 /*
2587 ================
2588 idAFEntity_SteamPipe::Spawn
2589 ================
2590 */
Spawn(void)2591 void idAFEntity_SteamPipe::Spawn( void ) {
2592 	idVec3 steamDir;
2593 	const char *steamBodyName;
2594 
2595 	LoadAF();
2596 
2597 	SetCombatModel();
2598 
2599 	SetPhysics( af.GetPhysics() );
2600 
2601 	fl.takedamage = true;
2602 
2603 	steamBodyName = spawnArgs.GetString( "steamBody", "" );
2604 	steamForce = spawnArgs.GetFloat( "steamForce", "2000" );
2605 	steamUpForce = spawnArgs.GetFloat( "steamUpForce", "10" );
2606 	steamDir = af.GetPhysics()->GetAxis( steamBody )[2];
2607 	steamBody = af.GetPhysics()->GetBodyId( steamBodyName );
2608 	force.SetPosition( af.GetPhysics(), steamBody, af.GetPhysics()->GetOrigin( steamBody ) );
2609 	force.SetForce( steamDir * -steamForce );
2610 
2611 	InitSteamRenderEntity();
2612 
2613 	BecomeActive( TH_THINK );
2614 }
2615 
2616 /*
2617 ================
2618 idAFEntity_SteamPipe::InitSteamRenderEntity
2619 ================
2620 */
InitSteamRenderEntity(void)2621 void idAFEntity_SteamPipe::InitSteamRenderEntity( void ) {
2622 	const char	*temp;
2623 	const idDeclModelDef *modelDef;
2624 
2625 	memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2626 	steamRenderEntity.shaderParms[ SHADERPARM_RED ]		= 1.0f;
2627 	steamRenderEntity.shaderParms[ SHADERPARM_GREEN ]	= 1.0f;
2628 	steamRenderEntity.shaderParms[ SHADERPARM_BLUE ]	= 1.0f;
2629 	modelDef = NULL;
2630 	temp = spawnArgs.GetString ( "model_steam" );
2631 	if ( *temp != '\0' ) {
2632 		if ( !strstr( temp, "." ) ) {
2633 			modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, temp, false ) );
2634 			if ( modelDef ) {
2635 				steamRenderEntity.hModel = modelDef->ModelHandle();
2636 			}
2637 		}
2638 
2639 		if ( !steamRenderEntity.hModel ) {
2640 			steamRenderEntity.hModel = renderModelManager->FindModel( temp );
2641 		}
2642 
2643 		if ( steamRenderEntity.hModel ) {
2644 			steamRenderEntity.bounds = steamRenderEntity.hModel->Bounds( &steamRenderEntity );
2645 		} else {
2646 			steamRenderEntity.bounds.Zero();
2647 		}
2648 		steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2649 		steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2650 		steamModelDefHandle = gameRenderWorld->AddEntityDef( &steamRenderEntity );
2651 	}
2652 }
2653 
2654 /*
2655 ================
2656 idAFEntity_SteamPipe::Think
2657 ================
2658 */
Think(void)2659 void idAFEntity_SteamPipe::Think( void ) {
2660 	idVec3 steamDir;
2661 
2662 	if ( thinkFlags & TH_THINK ) {
2663 		steamDir.x = gameLocal.random.CRandomFloat() * steamForce;
2664 		steamDir.y = gameLocal.random.CRandomFloat() * steamForce;
2665 		steamDir.z = steamUpForce;
2666 		force.SetForce( steamDir );
2667 		force.Evaluate( gameLocal.time );
2668 		//gameRenderWorld->DebugArrow( colorWhite, af.GetPhysics()->GetOrigin( steamBody ), af.GetPhysics()->GetOrigin( steamBody ) - 10.0f * steamDir, 4 );
2669 	}
2670 
2671 	if ( steamModelDefHandle >= 0 ){
2672 		steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2673 		steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2674 		gameRenderWorld->UpdateEntityDef( steamModelDefHandle, &steamRenderEntity );
2675 	}
2676 
2677 	idAFEntity_Base::Think();
2678 }
2679 
2680 
2681 /*
2682 ===============================================================================
2683 
2684   idAFEntity_ClawFourFingers
2685 
2686 ===============================================================================
2687 */
2688 
2689 const idEventDef EV_SetFingerAngle( "setFingerAngle", "f" );
2690 const idEventDef EV_StopFingers( "stopFingers" );
2691 
2692 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_ClawFourFingers )
2693 	EVENT( EV_SetFingerAngle,		idAFEntity_ClawFourFingers::Event_SetFingerAngle )
2694 	EVENT( EV_StopFingers,			idAFEntity_ClawFourFingers::Event_StopFingers )
2695 END_CLASS
2696 
2697 static const char *clawConstraintNames[] = {
2698 	"claw1", "claw2", "claw3", "claw4"
2699 };
2700 
2701 /*
2702 ================
2703 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers
2704 ================
2705 */
idAFEntity_ClawFourFingers(void)2706 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers( void ) {
2707 	fingers[0]	= NULL;
2708 	fingers[1]	= NULL;
2709 	fingers[2]	= NULL;
2710 	fingers[3]	= NULL;
2711 }
2712 
2713 /*
2714 ================
2715 idAFEntity_ClawFourFingers::Save
2716 ================
2717 */
Save(idSaveGame * savefile) const2718 void idAFEntity_ClawFourFingers::Save( idSaveGame *savefile ) const {
2719 	int i;
2720 
2721 	for ( i = 0; i < 4; i++ ) {
2722 		fingers[i]->Save( savefile );
2723 	}
2724 }
2725 
2726 /*
2727 ================
2728 idAFEntity_ClawFourFingers::Restore
2729 ================
2730 */
Restore(idRestoreGame * savefile)2731 void idAFEntity_ClawFourFingers::Restore( idRestoreGame *savefile ) {
2732 	int i;
2733 
2734 	for ( i = 0; i < 4; i++ ) {
2735 		fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2736 		fingers[i]->Restore( savefile );
2737 	}
2738 
2739 	SetCombatModel();
2740 	LinkCombat();
2741 }
2742 
2743 /*
2744 ================
2745 idAFEntity_ClawFourFingers::Spawn
2746 ================
2747 */
Spawn(void)2748 void idAFEntity_ClawFourFingers::Spawn( void ) {
2749 	int i;
2750 
2751 	LoadAF();
2752 
2753 	SetCombatModel();
2754 
2755 	af.GetPhysics()->LockWorldConstraints( true );
2756 	af.GetPhysics()->SetForcePushable( true );
2757 	SetPhysics( af.GetPhysics() );
2758 
2759 	fl.takedamage = true;
2760 
2761 	for ( i = 0; i < 4; i++ ) {
2762 		fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2763 		if ( !fingers[i] ) {
2764 			gameLocal.Error( "idClaw_FourFingers '%s': can't find claw constraint '%s'", name.c_str(), clawConstraintNames[i] );
2765 		}
2766 	}
2767 }
2768 
2769 /*
2770 ================
2771 idAFEntity_ClawFourFingers::Event_SetFingerAngle
2772 ================
2773 */
Event_SetFingerAngle(float angle)2774 void idAFEntity_ClawFourFingers::Event_SetFingerAngle( float angle ) {
2775 	int i;
2776 
2777 	for ( i = 0; i < 4; i++ ) {
2778 		fingers[i]->SetSteerAngle( angle );
2779 		fingers[i]->SetSteerSpeed( 0.5f );
2780 	}
2781 	af.GetPhysics()->Activate();
2782 }
2783 
2784 /*
2785 ================
2786 idAFEntity_ClawFourFingers::Event_StopFingers
2787 ================
2788 */
Event_StopFingers(void)2789 void idAFEntity_ClawFourFingers::Event_StopFingers( void ) {
2790 	int i;
2791 
2792 	for ( i = 0; i < 4; i++ ) {
2793 		fingers[i]->SetSteerAngle( fingers[i]->GetAngle() );
2794 	}
2795 }
2796 
2797 
2798 /*
2799 ===============================================================================
2800 
2801   editor support routines
2802 
2803 ===============================================================================
2804 */
2805 
2806 
2807 /*
2808 ================
2809 idGameEdit::AF_SpawnEntity
2810 ================
2811 */
AF_SpawnEntity(const char * fileName)2812 bool idGameEdit::AF_SpawnEntity( const char *fileName ) {
2813 	idDict args;
2814 	idPlayer *player;
2815 	idAFEntity_Generic *ent;
2816 	const idDeclAF *af;
2817 	idVec3 org;
2818 	float yaw;
2819 
2820 	player = gameLocal.GetLocalPlayer();
2821 	if ( !player || !gameLocal.CheatsOk( false ) ) {
2822 		return false;
2823 	}
2824 
2825 	af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, fileName ) );
2826 	if ( !af ) {
2827 		return false;
2828 	}
2829 
2830 	yaw = player->viewAngles.yaw;
2831 	args.Set( "angle", va( "%f", yaw + 180 ) );
2832 	org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
2833 	args.Set( "origin", org.ToString() );
2834 	args.Set( "spawnclass", "idAFEntity_Generic" );
2835 	if ( af->model[0] ) {
2836 		args.Set( "model", af->model.c_str() );
2837 	} else {
2838 		args.Set( "model", fileName );
2839 	}
2840 	if ( af->skin[0] ) {
2841 		args.Set( "skin", af->skin.c_str() );
2842 	}
2843 	args.Set( "articulatedFigure", fileName );
2844 	args.Set( "nodrop", "1" );
2845 	ent = static_cast<idAFEntity_Generic *>(gameLocal.SpawnEntityType( idAFEntity_Generic::Type, &args));
2846 
2847 	// always update this entity
2848 	ent->BecomeActive( TH_THINK );
2849 	ent->KeepRunningPhysics();
2850 	ent->fl.forcePhysicsUpdate = true;
2851 
2852 	player->dragEntity.SetSelected( ent );
2853 
2854 	return true;
2855 }
2856 
2857 /*
2858 ================
2859 idGameEdit::AF_UpdateEntities
2860 ================
2861 */
AF_UpdateEntities(const char * fileName)2862 void idGameEdit::AF_UpdateEntities( const char *fileName ) {
2863 	idEntity *ent;
2864 	idAFEntity_Base *af;
2865 	idStr name;
2866 
2867 	name = fileName;
2868 	name.StripFileExtension();
2869 
2870 	// reload any idAFEntity_Generic which uses the given articulated figure file
2871 	for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2872 		if ( ent->IsType( idAFEntity_Base::Type ) ) {
2873 			af = static_cast<idAFEntity_Base *>(ent);
2874 			if ( name.Icmp( af->GetAFName() ) == 0 ) {
2875 				af->LoadAF();
2876 				af->GetAFPhysics()->PutToRest();
2877 			}
2878 		}
2879 	}
2880 }
2881 
2882 /*
2883 ================
2884 idGameEdit::AF_UndoChanges
2885 ================
2886 */
AF_UndoChanges(void)2887 void idGameEdit::AF_UndoChanges( void ) {
2888 	int i, c;
2889 	idEntity *ent;
2890 	idAFEntity_Base *af;
2891 	idDeclAF *decl;
2892 
2893 	c = declManager->GetNumDecls( DECL_AF );
2894 	for ( i = 0; i < c; i++ ) {
2895 		decl = static_cast<idDeclAF *>( const_cast<idDecl *>( declManager->DeclByIndex( DECL_AF, i, false ) ) );
2896 		if ( !decl->modified ) {
2897 			continue;
2898 		}
2899 
2900 		decl->Invalidate();
2901 		declManager->FindType( DECL_AF, decl->GetName() );
2902 
2903 		// reload all AF entities using the file
2904 		for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2905 			if ( ent->IsType( idAFEntity_Base::Type ) ) {
2906 				af = static_cast<idAFEntity_Base *>(ent);
2907 				if ( idStr::Icmp( decl->GetName(), af->GetAFName() ) == 0 ) {
2908 					af->LoadAF();
2909 				}
2910 			}
2911 		}
2912 	}
2913 }
2914 
2915 /*
2916 ================
2917 GetJointTransform
2918 ================
2919 */
2920 typedef struct {
2921 	renderEntity_t *ent;
2922 	const idMD5Joint *joints;
2923 } jointTransformData_t;
2924 
GetJointTransform(void * model,const idJointMat * frame,const char * jointName,idVec3 & origin,idMat3 & axis)2925 static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
2926 	int i;
2927 	jointTransformData_t *data = reinterpret_cast<jointTransformData_t *>(model);
2928 
2929 	for ( i = 0; i < data->ent->numJoints; i++ ) {
2930 		if ( data->joints[i].name.Icmp( jointName ) == 0 ) {
2931 			break;
2932 		}
2933 	}
2934 	if ( i >= data->ent->numJoints ) {
2935 		return false;
2936 	}
2937 	origin = frame[i].ToVec3();
2938 	axis = frame[i].ToMat3();
2939 	return true;
2940 }
2941 
2942 /*
2943 ================
2944 GetArgString
2945 ================
2946 */
GetArgString(const idDict & args,const idDict * defArgs,const char * key)2947 static const char *GetArgString( const idDict &args, const idDict *defArgs, const char *key ) {
2948 	const char *s;
2949 
2950 	s = args.GetString( key );
2951 	if ( !s[0] && defArgs ) {
2952 		s = defArgs->GetString( key );
2953 	}
2954 	return s;
2955 }
2956 
2957 /*
2958 ================
2959 idGameEdit::AF_CreateMesh
2960 ================
2961 */
AF_CreateMesh(const idDict & args,idVec3 & meshOrigin,idMat3 & meshAxis,bool & poseIsSet)2962 idRenderModel *idGameEdit::AF_CreateMesh( const idDict &args, idVec3 &meshOrigin, idMat3 &meshAxis, bool &poseIsSet ) {
2963 	int i, jointNum;
2964 	const idDeclAF *af;
2965 	const idDeclAF_Body *fb;
2966 	renderEntity_t ent;
2967 	idVec3 origin, *bodyOrigin, *newBodyOrigin, *modifiedOrigin;
2968 	idMat3 axis, *bodyAxis, *newBodyAxis, *modifiedAxis;
2969 	declAFJointMod_t *jointMod;
2970 	idAngles angles;
2971 	const idDict *defArgs;
2972 	const idKeyValue *arg;
2973 	idStr name;
2974 	jointTransformData_t data;
2975 	const char *classname, *afName, *modelName;
2976 	idRenderModel *md5;
2977 	const idDeclModelDef *modelDef;
2978 	const idMD5Anim *MD5anim;
2979 	const idMD5Joint *MD5joint;
2980 	const idMD5Joint *MD5joints;
2981 	int numMD5joints;
2982 	idJointMat *originalJoints;
2983 	int parentNum;
2984 
2985 	poseIsSet = false;
2986 	meshOrigin.Zero();
2987 	meshAxis.Identity();
2988 
2989 	classname = args.GetString( "classname" );
2990 	defArgs = gameLocal.FindEntityDefDict( classname );
2991 
2992 	// get the articulated figure
2993 	afName = GetArgString( args, defArgs, "articulatedFigure" );
2994 	af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, afName ) );
2995 	if ( !af ) {
2996 		return NULL;
2997 	}
2998 
2999 	// get the md5 model
3000 	modelName = GetArgString( args, defArgs, "model" );
3001 	modelDef = static_cast< const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
3002 	if ( !modelDef ) {
3003 		return NULL;
3004 	}
3005 
3006 	// make sure model hasn't been purged
3007 	if ( modelDef->ModelHandle() && !modelDef->ModelHandle()->IsLoaded() ) {
3008 		modelDef->ModelHandle()->LoadModel();
3009 	}
3010 
3011 	// get the md5
3012 	md5 = modelDef->ModelHandle();
3013 	if ( !md5 || md5->IsDefaultModel() ) {
3014 		return NULL;
3015 	}
3016 
3017 	// get the articulated figure pose anim
3018 	int animNum = modelDef->GetAnim( "af_pose" );
3019 	if ( !animNum ) {
3020 		return NULL;
3021 	}
3022 	const idAnim *anim = modelDef->GetAnim( animNum );
3023 	if ( !anim ) {
3024 		return NULL;
3025 	}
3026 	MD5anim = anim->MD5Anim( 0 );
3027 	MD5joints = md5->GetJoints();
3028 	numMD5joints = md5->NumJoints();
3029 
3030 	// setup a render entity
3031 	memset( &ent, 0, sizeof( ent ) );
3032 	ent.customSkin = modelDef->GetSkin();
3033 	ent.bounds.Clear();
3034 	ent.numJoints = numMD5joints;
3035 	ent.joints = ( idJointMat * )_alloca16( ent.numJoints * sizeof( *ent.joints ) );
3036 
3037 	// create animation from of the af_pose
3038 	ANIM_CreateAnimFrame( md5, MD5anim, ent.numJoints, ent.joints, 1, modelDef->GetVisualOffset(), false );
3039 
3040 	// buffers to store the initial origin and axis for each body
3041 	bodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
3042 	bodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
3043 	newBodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
3044 	newBodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
3045 
3046 	// finish the AF positions
3047 	data.ent = &ent;
3048 	data.joints = MD5joints;
3049 	af->Finish( GetJointTransform, ent.joints, &data );
3050 
3051 	// get the initial origin and axis for each AF body
3052 	for ( i = 0; i < af->bodies.Num(); i++ ) {
3053 		fb = af->bodies[i];
3054 
3055 		if ( fb->modelType == TRM_BONE ) {
3056 			// axis of bone trace model
3057 			axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
3058 			axis[2].Normalize();
3059 			axis[2].NormalVectors( axis[0], axis[1] );
3060 			axis[1] = -axis[1];
3061 		} else {
3062 			axis = fb->angles.ToMat3();
3063 		}
3064 
3065 		newBodyOrigin[i] = bodyOrigin[i] = fb->origin.ToVec3();
3066 		newBodyAxis[i] = bodyAxis[i] = axis;
3067 	}
3068 
3069 	// get any new body transforms stored in the key/value pairs
3070 	for ( arg = args.MatchPrefix( "body ", NULL ); arg; arg = args.MatchPrefix( "body ", arg ) ) {
3071 		name = arg->GetKey();
3072 		name.Strip( "body " );
3073 		for ( i = 0; i < af->bodies.Num(); i++ ) {
3074 			fb = af->bodies[i];
3075 			if ( fb->name.Icmp( name ) == 0 ) {
3076 				break;
3077 			}
3078 		}
3079 		if ( i >= af->bodies.Num() ) {
3080 			continue;
3081 		}
3082 		sscanf( arg->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
3083 
3084 		if ( fb->jointName.Icmp( "origin" ) == 0 ) {
3085 			meshAxis = bodyAxis[i].Transpose() * angles.ToMat3();
3086 			meshOrigin = origin - bodyOrigin[i] * meshAxis;
3087 			poseIsSet = true;
3088 		} else {
3089 			newBodyOrigin[i] = origin;
3090 			newBodyAxis[i] = angles.ToMat3();
3091 		}
3092 	}
3093 
3094 	// save the original joints
3095 	originalJoints = ( idJointMat * )_alloca16( numMD5joints * sizeof( originalJoints[0] ) );
3096 	memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) );
3097 
3098 	// buffer to store the joint mods
3099 	jointMod = (declAFJointMod_t *) _alloca16( numMD5joints * sizeof( declAFJointMod_t ) );
3100 	memset( jointMod, -1, numMD5joints * sizeof( declAFJointMod_t ) );
3101 	modifiedOrigin = (idVec3 *) _alloca16( numMD5joints * sizeof( idVec3 ) );
3102 	memset( modifiedOrigin, 0, numMD5joints * sizeof( idVec3 ) );
3103 	modifiedAxis = (idMat3 *) _alloca16( numMD5joints * sizeof( idMat3 ) );
3104 	memset( modifiedAxis, 0, numMD5joints * sizeof( idMat3 ) );
3105 
3106 	// get all the joint modifications
3107 	for ( i = 0; i < af->bodies.Num(); i++ ) {
3108 		fb = af->bodies[i];
3109 
3110 		if ( fb->jointName.Icmp( "origin" ) == 0 ) {
3111 			continue;
3112 		}
3113 
3114 		for ( jointNum = 0; jointNum < numMD5joints; jointNum++ ) {
3115 			if ( MD5joints[jointNum].name.Icmp( fb->jointName ) == 0 ) {
3116 				break;
3117 			}
3118 		}
3119 
3120 		if ( jointNum >= 0 && jointNum < ent.numJoints ) {
3121 			jointMod[ jointNum ] = fb->jointMod;
3122 			modifiedAxis[ jointNum ] = ( bodyAxis[i] * originalJoints[jointNum].ToMat3().Transpose() ).Transpose() * ( newBodyAxis[i] * meshAxis.Transpose() );
3123 			// FIXME: calculate correct modifiedOrigin
3124 			modifiedOrigin[ jointNum ] = originalJoints[ jointNum ].ToVec3();
3125 		}
3126 	}
3127 
3128 	// apply joint modifications to the skeleton
3129 	MD5joint = MD5joints + 1;
3130 	for( i = 1; i < numMD5joints; i++, MD5joint++ ) {
3131 
3132 		parentNum = MD5joint->parent - MD5joints;
3133 		idMat3 parentAxis = originalJoints[ parentNum ].ToMat3();
3134 		idMat3 localm = originalJoints[i].ToMat3() * parentAxis.Transpose();
3135 		idVec3 localt = ( originalJoints[i].ToVec3() - originalJoints[ parentNum ].ToVec3() ) * parentAxis.Transpose();
3136 
3137 		switch( jointMod[i] ) {
3138 			case DECLAF_JOINTMOD_ORIGIN: {
3139 				ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
3140 				ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
3141 				break;
3142 			}
3143 			case DECLAF_JOINTMOD_AXIS: {
3144 				ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
3145 				ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
3146 				break;
3147 			}
3148 			case DECLAF_JOINTMOD_BOTH: {
3149 				ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
3150 				ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
3151 				break;
3152 			}
3153 			default: {
3154 				ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
3155 				ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
3156 				break;
3157 			}
3158 		}
3159 	}
3160 
3161 	// instantiate a mesh using the joint information from the render entity
3162 	return md5->InstantiateDynamicModel( &ent, NULL, NULL );
3163 }
3164 
3165 #ifdef _D3XP
3166 
3167 /*
3168 ===============================================================================
3169 idHarvestable
3170 ===============================================================================
3171 */
3172 
3173 const idEventDef EV_Harvest_SpawnHarvestTrigger( "<spawnHarvestTrigger>", NULL );
3174 
CLASS_DECLARATION(idEntity,idHarvestable)3175 CLASS_DECLARATION( idEntity, idHarvestable )
3176 EVENT( EV_Harvest_SpawnHarvestTrigger,	idHarvestable::Event_SpawnHarvestTrigger )
3177 EVENT( EV_Touch,						idHarvestable::Event_Touch )
3178 END_CLASS
3179 
3180 idHarvestable::idHarvestable() {
3181 	trigger = NULL;
3182 	parentEnt = NULL;
3183 }
3184 
~idHarvestable()3185 idHarvestable::~idHarvestable() {
3186 	if ( trigger ) {
3187 		delete trigger;
3188 		trigger = NULL;
3189 	}
3190 }
3191 
Spawn()3192 void idHarvestable::Spawn() {
3193 
3194 	startTime = 0;
3195 
3196 	spawnArgs.GetFloat( "triggersize", "120", triggersize );
3197 	spawnArgs.GetFloat( "give_delay", "3", giveDelay);
3198 	giveDelay *= 1000;
3199 	given = false;
3200 
3201 	removeDelay = spawnArgs.GetFloat( "remove_delay") * 1000.0f;
3202 
3203 	fxFollowPlayer = spawnArgs.GetBool("fx_follow_player", "1");
3204 	fxOrient = spawnArgs.GetString("fx_orient");
3205 
3206 
3207 }
3208 
Init(idEntity * parent)3209 void idHarvestable::Init(idEntity* parent) {
3210 
3211 	assert(parent);
3212 
3213 	parentEnt = parent;
3214 
3215 	GetPhysics()->SetOrigin( parent->GetPhysics()->GetOrigin() );
3216 	this->Bind(parent, true);
3217 
3218 	//Set the skin of the entity to the harvest skin
3219 	idStr skin = parent->spawnArgs.GetString("skin_harvest", "");
3220 	if(skin.Length()) {
3221 		parent->SetSkin(declManager->FindSkin(skin.c_str()));
3222 	}
3223 
3224 	idEntity* head = NULL;
3225 	if(parent->IsType(idActor::Type)) {
3226 		idActor* withHead = (idActor*)parent;
3227 		head = withHead->GetHeadEntity();
3228 	}
3229 	if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
3230 		idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
3231 		head = withHead->head.GetEntity();
3232 	}
3233 	if(head) {
3234 		idStr headskin = parent->spawnArgs.GetString("skin_harvest_head", "");
3235 		if(headskin.Length()) {
3236 			head->SetSkin(declManager->FindSkin(headskin.c_str()));
3237 		}
3238 	}
3239 
3240 	idStr sound = parent->spawnArgs.GetString("harvest_sound");
3241 	if(sound.Length() > 0) {
3242 		 parent->StartSound( sound.c_str(), SND_CHANNEL_ANY, 0, false, NULL);
3243 	}
3244 
3245 
3246 	PostEventMS( &EV_Harvest_SpawnHarvestTrigger, 0 );
3247 }
3248 
Save(idSaveGame * savefile) const3249 void idHarvestable::Save( idSaveGame *savefile ) const {
3250 	savefile->WriteFloat( triggersize );
3251 	savefile->WriteClipModel( trigger );
3252 	savefile->WriteFloat( giveDelay );
3253 	savefile->WriteFloat( removeDelay );
3254 	savefile->WriteBool( given );
3255 
3256 	player.Save( savefile );
3257 	savefile->WriteInt( startTime );
3258 
3259 	savefile->WriteBool( fxFollowPlayer );
3260 	fx.Save( savefile );
3261 	savefile->WriteString( fxOrient );
3262 
3263 	parentEnt.Save(savefile);
3264 }
3265 
Restore(idRestoreGame * savefile)3266 void idHarvestable::Restore( idRestoreGame *savefile ) {
3267 	savefile->ReadFloat( triggersize );
3268 	savefile->ReadClipModel( trigger );
3269 	savefile->ReadFloat( giveDelay );
3270 	savefile->ReadFloat( removeDelay );
3271 	savefile->ReadBool( given );
3272 
3273 	player.Restore( savefile );
3274 	savefile->ReadInt( startTime );
3275 
3276 	savefile->ReadBool( fxFollowPlayer );
3277 	fx.Restore( savefile );
3278 	savefile->ReadString( fxOrient );
3279 
3280 	parentEnt.Restore(savefile);
3281 }
3282 
SetParent(idEntity * parent)3283 void idHarvestable::SetParent(idEntity* parent) {
3284 	parentEnt = parent;
3285 }
3286 
Think()3287 void idHarvestable::Think() {
3288 
3289 	idEntity* parent = parentEnt.GetEntity();
3290 	if(!parent) {
3291 		return;
3292 	}
3293 
3294 	//Update the orientation of the box
3295 	if(trigger && parent && !parent->GetPhysics()->IsAtRest()) {
3296 		trigger->Link( gameLocal.clip, this, 0, parent->GetPhysics()->GetOrigin(), parent->GetPhysics()->GetAxis());
3297 	}
3298 
3299 	if(startTime && gameLocal.slow.time - startTime > giveDelay && ! given) {
3300 		idPlayer *thePlayer = player.GetEntity();
3301 
3302 		thePlayer->Give(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"));
3303 		thePlayer->harvest_lock = false;
3304 		given = true;
3305 	}
3306 
3307 	if(startTime && gameLocal.slow.time - startTime > removeDelay) {
3308 		parent->PostEventMS( &EV_Remove, 0 );
3309 		PostEventMS( &EV_Remove, 0 );
3310 	}
3311 
3312 	if(fxFollowPlayer) {
3313 		idEntityFx* fxEnt = fx.GetEntity();
3314 
3315 		if(fxEnt) {
3316 			idMat3 orientAxisLocal;
3317 			if(GetFxOrientationAxis(orientAxisLocal)) {
3318 				//gameRenderWorld->DebugAxis(fxEnt->GetPhysics()->GetOrigin(), orientAxisLocal);
3319 				fxEnt->GetPhysics()->SetAxis(orientAxisLocal);
3320 			}
3321 		}
3322 	}
3323 }
3324 
3325 /*
3326 ================
3327 idAFEntity_Harvest::Gib
3328 Called when the parent object has been gibbed.
3329 ================
3330 */
Gib()3331 void idHarvestable::Gib() {
3332 	//Stop any looping sound that was playing
3333 	idEntity* parent = parentEnt.GetEntity();
3334 	if(parent) {
3335 		idStr sound = parent->spawnArgs.GetString("harvest_sound");
3336 		if(sound.Length() > 0) {
3337 			parent->StopSound(SND_CHANNEL_ANY, false);
3338 		}
3339 	}
3340 }
3341 
3342 /*
3343 ================
3344 idAFEntity_Harvest::BeginBurn
3345 ================
3346 */
BeginBurn()3347 void idHarvestable::BeginBurn() {
3348 
3349 	idEntity* parent = parentEnt.GetEntity();
3350 	if(!parent) {
3351 		return;
3352 	}
3353 
3354 	if(!spawnArgs.GetBool("burn")) {
3355 		return;
3356 	}
3357 
3358 
3359 	//Switch Skins if the parent would like us to.
3360 	idStr skin = parent->spawnArgs.GetString("skin_harvest_burn", "");
3361 	if(skin.Length()) {
3362 		parent->SetSkin(declManager->FindSkin(skin.c_str()));
3363 	}
3364 	parent->GetRenderEntity()->noShadow = true;
3365 	parent->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
3366 
3367 	idEntity* head = NULL;
3368 	if(parent->IsType(idActor::Type)) {
3369 		idActor* withHead = (idActor*)parent;
3370 		head = withHead->GetHeadEntity();
3371 	}
3372 	if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
3373 		idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
3374 		head = withHead->head.GetEntity();
3375 	}
3376 	if(head) {
3377 		idStr headskin = parent->spawnArgs.GetString("skin_harvest_burn_head", "");
3378 		if(headskin.Length()) {
3379 			head->SetSkin(declManager->FindSkin(headskin.c_str()));
3380 		}
3381 
3382 		head->GetRenderEntity()->noShadow = true;
3383 		head->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
3384 	}
3385 
3386 
3387 
3388 }
3389 
3390 /*
3391 ================
3392 idAFEntity_Harvest::BeginFX
3393 ================
3394 */
BeginFX()3395 void idHarvestable::BeginFX() {
3396 	if(strlen(spawnArgs.GetString("fx")) <= 0) {
3397 		return;
3398 	}
3399 
3400 	idMat3* orientAxis = NULL;
3401 	idMat3 orientAxisLocal;
3402 
3403 	if(GetFxOrientationAxis(orientAxisLocal)) {
3404 		orientAxis = &orientAxisLocal;
3405 	}
3406 	fx = idEntityFx::StartFx( spawnArgs.GetString("fx"), NULL, orientAxis, this, spawnArgs.GetBool("fx_bind") );
3407 }
3408 
3409 /*
3410 ================
3411 idAFEntity_Harvest::CalcTriggerBounds
3412 ================
3413 */
CalcTriggerBounds(float size,idBounds & bounds)3414 void idHarvestable::CalcTriggerBounds( float size, idBounds &bounds ) {
3415 
3416 	idEntity* parent = parentEnt.GetEntity();
3417 	if(!parent) {
3418 		return;
3419 	}
3420 
3421 	//Simple trigger bounds is the absolute bounds of the AF plus a defined size
3422 	bounds = parent->GetPhysics()->GetAbsBounds();
3423 	bounds.ExpandSelf(size);
3424 	bounds[0] -= parent->GetPhysics()->GetOrigin();
3425 	bounds[1] -= parent->GetPhysics()->GetOrigin();
3426 }
3427 
GetFxOrientationAxis(idMat3 & mat)3428 bool idHarvestable::GetFxOrientationAxis(idMat3& mat) {
3429 
3430 	idEntity* parent = parentEnt.GetEntity();
3431 	if(!parent) {
3432 		return false;
3433 	}
3434 
3435 	idPlayer *thePlayer = player.GetEntity();
3436 
3437 	if(!fxOrient.Icmp("up")) {
3438 		//Orient up
3439 		idVec3 grav = parent->GetPhysics()->GetGravityNormal()*-1;
3440 		idVec3 left, up;
3441 
3442 		grav.OrthogonalBasis(left, up);
3443 		idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, grav.x, grav.y, grav.z);
3444 		mat = temp;
3445 
3446 		return true;
3447 
3448 	} else if(!fxOrient.Icmp("weapon")) {
3449 		//Orient the fx towards the muzzle of the weapon
3450 		jointHandle_t	joint;
3451 		idVec3	joint_origin;
3452 		idMat3	joint_axis;
3453 
3454 		joint = thePlayer->weapon.GetEntity()->GetAnimator()->GetJointHandle( spawnArgs.GetString("fx_weapon_joint") );
3455 		if ( joint != INVALID_JOINT ) {
3456 			thePlayer->weapon.GetEntity()->GetJointWorldTransform( joint, gameLocal.slow.time, joint_origin, joint_axis );
3457 		} else {
3458 			joint_origin = thePlayer->GetPhysics()->GetOrigin();
3459 		}
3460 
3461 		idVec3 toPlayer = joint_origin-parent->GetPhysics()->GetOrigin();
3462 		toPlayer.NormalizeFast();
3463 
3464 		idVec3 left, up;
3465 		toPlayer.OrthogonalBasis(left, up);
3466 		idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
3467 		mat = temp;
3468 
3469 		return true;
3470 
3471 	} else if(!fxOrient.Icmp("player")) {
3472 		//Orient the fx towards the eye of the player
3473 		idVec3 eye = thePlayer->GetEyePosition();
3474 		idVec3 toPlayer = eye-parent->GetPhysics()->GetOrigin();
3475 
3476 		toPlayer.Normalize();
3477 
3478 		idVec3 left, up;
3479 		up.Set(0, 1, 0);
3480 		left = toPlayer.Cross(up);
3481 		up = left.Cross(toPlayer);
3482 
3483 
3484 		//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 );
3485 
3486 		idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
3487 
3488 		mat = temp;
3489 
3490 		return true;
3491 	}
3492 
3493 	//Returning false indicates that the orientation is not used;
3494 	return false;
3495 }
3496 
3497 /*
3498 ================
3499 idAFEntity_Harvest::Event_SpawnHarvestTrigger
3500 ================
3501 */
Event_SpawnHarvestTrigger(void)3502 void idHarvestable::Event_SpawnHarvestTrigger( void ) {
3503 	idBounds		bounds;
3504 
3505 	idEntity* parent = parentEnt.GetEntity();
3506 	if(!parent) {
3507 		return;
3508 	}
3509 
3510 	CalcTriggerBounds( triggersize, bounds );
3511 
3512 	// create a trigger clip model
3513 	trigger = new idClipModel( idTraceModel( bounds ) );
3514 	trigger->Link( gameLocal.clip, this, 255, parent->GetPhysics()->GetOrigin(), mat3_identity);
3515 	trigger->SetContents( CONTENTS_TRIGGER );
3516 
3517 	startTime = 0;
3518 }
3519 
3520 /*
3521 ================
3522 idAFEntity_Harvest::Event_Touch
3523 ================
3524 */
Event_Touch(idEntity * other,trace_t * trace)3525 void idHarvestable::Event_Touch( idEntity *other, trace_t *trace ) {
3526 
3527 	idEntity* parent = parentEnt.GetEntity();
3528 	if(!parent) {
3529 		return;
3530 	}
3531 	if(parent->IsType(idAFEntity_Gibbable::Type)) {
3532 		idAFEntity_Gibbable* gibParent = (idAFEntity_Gibbable*)parent;
3533 		if(gibParent->IsGibbed())
3534 			return;
3535 	}
3536 
3537 
3538 	if(!startTime && other && other->IsType(idPlayer::Type)) {
3539 		idPlayer *thePlayer = static_cast<idPlayer *>(other);
3540 
3541 		if(thePlayer->harvest_lock) {
3542 			//Don't harvest if the player is in mid harvest
3543 			return;
3544 		}
3545 
3546 		player = thePlayer;
3547 
3548 		bool okToGive = true;
3549 		idStr requiredWeapons = spawnArgs.GetString("required_weapons");
3550 
3551 		if(requiredWeapons.Length() > 0) {
3552 			idStr playerWeap = thePlayer->GetCurrentWeapon();
3553 			if(playerWeap.Length() == 0 || requiredWeapons.Find(playerWeap, false) == -1) {
3554 				okToGive = false;
3555 			}
3556 		}
3557 
3558 		if(okToGive) {
3559 			if(thePlayer->CanGive(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"))) {
3560 
3561 				startTime = gameLocal.slow.time;
3562 
3563 				//Lock the player from harvesting to prevent multiple harvests when only one is needed
3564 				thePlayer->harvest_lock = true;
3565 
3566 				idWeapon* weap = (idWeapon*)thePlayer->weapon.GetEntity();
3567 				if(weap) {
3568 					//weap->PostEventMS(&EV_Weapon_State, 0, "Charge", 8);
3569 					weap->ProcessEvent(&EV_Weapon_State, "Charge", 8);
3570 				}
3571 
3572 				BeginBurn();
3573 				BeginFX();
3574 
3575 				//Stop any looping sound that was playing
3576 				idStr sound = parent->spawnArgs.GetString("harvest_sound");
3577 				if(sound.Length() > 0) {
3578 					parent->StopSound(SND_CHANNEL_ANY, false);
3579 				}
3580 
3581 				//Make the parent object non-solid
3582 				parent->GetPhysics()->SetContents( 0 );
3583 				parent->GetPhysics()->GetClipModel()->Unlink();
3584 
3585 				//Turn of the trigger so it doesn't process twice
3586 				trigger->SetContents( 0 );
3587 			}
3588 		}
3589 	}
3590 }
3591 
3592 
3593 /*
3594 ===============================================================================
3595 
3596 idAFEntity_Harvest
3597 
3598 ===============================================================================
3599 */
3600 
3601 const idEventDef EV_Harvest_SpawnHarvestEntity( "<spawnHarvestEntity>", NULL );
3602 
CLASS_DECLARATION(idAFEntity_WithAttachedHead,idAFEntity_Harvest)3603 CLASS_DECLARATION( idAFEntity_WithAttachedHead, idAFEntity_Harvest )
3604 EVENT( EV_Harvest_SpawnHarvestEntity,	idAFEntity_Harvest::Event_SpawnHarvestEntity )
3605 END_CLASS
3606 
3607 /*
3608 ================
3609 idAFEntity_Harvest::idAFEntity_Harvest
3610 ================
3611 */
3612 idAFEntity_Harvest::idAFEntity_Harvest() {
3613 	harvestEnt = NULL;
3614 }
3615 
3616 /*
3617 ================
3618 idAFEntity_Harvest::~idAFEntity_Harvest
3619 ================
3620 */
~idAFEntity_Harvest()3621 idAFEntity_Harvest::~idAFEntity_Harvest() {
3622 
3623 	if ( harvestEnt.GetEntity() ) {
3624 		harvestEnt.GetEntity()->PostEventMS( &EV_Remove, 0 );
3625 	}
3626 
3627 }
3628 
3629 /*
3630 ================
3631 idAFEntity_Harvest::Save
3632 ================
3633 */
Save(idSaveGame * savefile) const3634 void idAFEntity_Harvest::Save( idSaveGame *savefile ) const {
3635 	harvestEnt.Save(savefile);
3636 }
3637 
3638 /*
3639 ================
3640 idAFEntity_Harvest::Restore
3641 ================
3642 */
Restore(idRestoreGame * savefile)3643 void idAFEntity_Harvest::Restore( idRestoreGame *savefile ) {
3644 	harvestEnt.Restore(savefile);
3645 	//if(harvestEnt.GetEntity()) {
3646 	//	harvestEnt.GetEntity()->SetParent(this);
3647 	//}
3648 }
3649 
3650 /*
3651 ================
3652 idAFEntity_Harvest::Spawn
3653 ================
3654 */
Spawn(void)3655 void idAFEntity_Harvest::Spawn( void ) {
3656 
3657 	PostEventMS( &EV_Harvest_SpawnHarvestEntity, 0 );
3658 }
3659 
3660 /*
3661 ================
3662 idAFEntity_Harvest::Think
3663 ================
3664 */
Think(void)3665 void idAFEntity_Harvest::Think( void ) {
3666 
3667 	idAFEntity_WithAttachedHead::Think();
3668 
3669 }
3670 
Event_SpawnHarvestEntity(void)3671 void idAFEntity_Harvest::Event_SpawnHarvestEntity( void ) {
3672 
3673 	const idDict *harvestDef = gameLocal.FindEntityDefDict( spawnArgs.GetString("def_harvest_type"), false );
3674 	if ( harvestDef ) {
3675 		idEntity *temp;
3676 		gameLocal.SpawnEntityDef( *harvestDef, &temp, false );
3677 		harvestEnt = static_cast<idHarvestable *>(temp);
3678 	}
3679 
3680 	if(harvestEnt.GetEntity()) {
3681 		//Let the harvest entity set itself up
3682 		harvestEnt.GetEntity()->Init(this);
3683 		harvestEnt.GetEntity()->BecomeActive( TH_THINK );
3684 	}
3685 }
3686 
Gib(const idVec3 & dir,const char * damageDefName)3687 void idAFEntity_Harvest::Gib( const idVec3 &dir, const char *damageDefName ) {
3688 	if(harvestEnt.GetEntity()) {
3689 		//Let the harvest ent know that we gibbed
3690 		harvestEnt.GetEntity()->Gib();
3691 	}
3692 	idAFEntity_WithAttachedHead::Gib(dir, damageDefName);
3693 }
3694 
3695 #endif
3696