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