2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
36 ===============================================================================
40 ===============================================================================
43 CLASS_DECLARATION( idEntity, idMultiModelAF )
51 void idMultiModelAF::Spawn( void ) {
52 physicsObj.SetSelf( this );
57 idMultiModelAF::~idMultiModelAF
60 idMultiModelAF::~idMultiModelAF( void ) {
63 for ( i = 0; i < modelDefHandles.Num(); i++ ) {
64 if ( modelDefHandles[i] != -1 ) {
65 gameRenderWorld->FreeEntityDef( modelDefHandles[i] );
66 modelDefHandles[i] = -1;
73 idMultiModelAF::SetModelForId
76 void idMultiModelAF::SetModelForId( int id, const idStr &modelName ) {
77 modelHandles.AssureSize( id+1, NULL );
78 modelDefHandles.AssureSize( id+1, -1 );
79 modelHandles[id] = renderModelManager->FindModel( modelName );
84 idMultiModelAF::Present
87 void idMultiModelAF::Present( void ) {
90 // don't present to the renderer if the entity hasn't changed
91 if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
94 BecomeInactive( TH_UPDATEVISUALS );
96 for ( i = 0; i < modelHandles.Num(); i++ ) {
98 if ( !modelHandles[i] ) {
102 renderEntity.origin = physicsObj.GetOrigin( i );
103 renderEntity.axis = physicsObj.GetAxis( i );
104 renderEntity.hModel = modelHandles[i];
105 renderEntity.bodyId = i;
107 // add to refresh list
108 if ( modelDefHandles[i] == -1 ) {
109 modelDefHandles[i] = gameRenderWorld->AddEntityDef( &renderEntity );
111 gameRenderWorld->UpdateEntityDef( modelDefHandles[i], &renderEntity );
118 idMultiModelAF::Think
121 void idMultiModelAF::Think( void ) {
128 ===============================================================================
132 ===============================================================================
135 CLASS_DECLARATION( idMultiModelAF, idChain )
142 builds a chain hanging down from the ceiling
143 the highest link is a child of the link below it etc.
144 this allows an object to be attached to multiple chains while keeping a single tree structure
147 void idChain::BuildChain( const idStr &name, const idVec3 &origin, float linkLength, float linkWidth, float density, int numLinks, bool bindToWorld ) {
149 float halfLinkLength = linkLength * 0.5f;
152 idAFBody *body, *lastBody;
153 idAFConstraint_BallAndSocketJoint *bsj;
154 idAFConstraint_UniversalJoint *uj;
157 // create a trace model
158 trm = idTraceModel( linkLength, linkWidth );
159 trm.Translate( -trm.offset );
161 org = origin - idVec3( 0, 0, halfLinkLength );
164 for ( i = 0; i < numLinks; i++ ) {
167 clip = new idClipModel( trm );
168 clip->SetContents( CONTENTS_SOLID );
169 clip->Link( gameLocal.clip, this, 0, org, mat3_identity );
170 body = new idAFBody( name + idStr(i), clip, density );
171 physicsObj.AddBody( body );
173 // visual model for body
174 SetModelForId( physicsObj.GetBodyId( body ), spawnArgs.GetString( "model" ) );
179 uj = new idAFConstraint_UniversalJoint( name + idStr(i), body, lastBody );
180 uj->SetShafts( idVec3( 0, 0, -1 ), idVec3( 0, 0, 1 ) );
181 //uj->SetConeLimit( idVec3( 0, 0, -1 ), 30.0f );
182 //uj->SetPyramidLimit( idVec3( 0, 0, -1 ), idVec3( 1, 0, 0 ), 90.0f, 30.0f );
185 uj = new idAFConstraint_UniversalJoint( name + idStr(i), lastBody, body );
186 uj->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) );
187 //uj->SetConeLimit( idVec3( 0, 0, 1 ), 30.0f );
189 uj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
190 uj->SetFriction( 0.9f );
191 physicsObj.AddConstraint( uj );
195 bsj = new idAFConstraint_BallAndSocketJoint( "joint" + idStr(i), lastBody, body );
196 bsj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
197 bsj->SetConeLimit( idVec3( 0, 0, 1 ), 60.0f, idVec3( 0, 0, 1 ) );
198 physicsObj.AddConstraint( bsj );
202 org[2] -= linkLength;
213 void idChain::Spawn( void ) {
215 float length, linkLength, linkWidth, density;
219 spawnArgs.GetBool( "drop", "0", drop );
220 spawnArgs.GetInt( "links", "3", numLinks );
221 spawnArgs.GetFloat( "length", idStr( numLinks * 32.0f ), length );
222 spawnArgs.GetFloat( "width", "8", linkWidth );
223 spawnArgs.GetFloat( "density", "0.2", density );
224 linkLength = length / numLinks;
225 origin = GetPhysics()->GetOrigin();
227 // initialize physics
228 physicsObj.SetSelf( this );
229 physicsObj.SetGravity( gameLocal.GetGravity() );
230 physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY );
231 SetPhysics( &physicsObj );
233 BuildChain( "link", origin, linkLength, linkWidth, density, numLinks, !drop );
237 ===============================================================================
241 ===============================================================================
244 CLASS_DECLARATION( idAnimatedEntity, idAFAttachment )
248 =====================
249 idAFAttachment::idAFAttachment
250 =====================
252 idAFAttachment::idAFAttachment( void ) {
256 attachJoint = INVALID_JOINT;
260 =====================
261 idAFAttachment::~idAFAttachment
262 =====================
264 idAFAttachment::~idAFAttachment( void ) {
266 StopSound( SND_CHANNEL_ANY, false );
273 =====================
274 idAFAttachment::Spawn
275 =====================
277 void idAFAttachment::Spawn( void ) {
278 idleAnim = animator.GetAnim( "idle" );
282 =====================
283 idAFAttachment::SetBody
284 =====================
286 void idAFAttachment::SetBody( idEntity *bodyEnt, const char *model, jointHandle_t attachJoint ) {
290 this->attachJoint = attachJoint;
292 fl.takedamage = true;
294 bleed = body->spawnArgs.GetBool( "bleed" );
295 spawnArgs.SetBool( "bleed", bleed );
299 =====================
300 idAFAttachment::ClearBody
301 =====================
303 void idAFAttachment::ClearBody( void ) {
305 attachJoint = INVALID_JOINT;
310 =====================
311 idAFAttachment::GetBody
312 =====================
314 idEntity *idAFAttachment::GetBody( void ) const {
322 archive object for savegame file
325 void idAFAttachment::Save( idSaveGame *savefile ) const {
326 savefile->WriteObject( body );
327 savefile->WriteInt( idleAnim );
328 savefile->WriteJoint( attachJoint );
333 idAFAttachment::Restore
335 unarchives object from save game file
338 void idAFAttachment::Restore( idRestoreGame *savefile ) {
339 savefile->ReadObject( reinterpret_cast<idClass *&>( body ) );
340 savefile->ReadInt( idleAnim );
341 savefile->ReadJoint( attachJoint );
352 void idAFAttachment::Hide( void ) {
362 void idAFAttachment::Show( void ) {
369 idAFAttachment::Damage
371 Pass damage to body at the bindjoint
374 void idAFAttachment::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
375 const char *damageDefName, const float damageScale, const int location ) {
378 body->Damage( inflictor, attacker, dir, damageDefName, damageScale, attachJoint );
384 idAFAttachment::AddDamageEffect
387 void idAFAttachment::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
389 trace_t c = collision;
390 c.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint );
391 body->AddDamageEffect( c, velocity, damageDefName );
397 idAFAttachment::GetImpactInfo
400 void idAFAttachment::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
402 body->GetImpactInfo( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, info );
404 idEntity::GetImpactInfo( ent, id, point, info );
410 idAFAttachment::ApplyImpulse
413 void idAFAttachment::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
415 body->ApplyImpulse( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, impulse );
417 idEntity::ApplyImpulse( ent, id, point, impulse );
423 idAFAttachment::AddForce
426 void idAFAttachment::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
428 body->AddForce( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, force );
430 idEntity::AddForce( ent, id, point, force );
436 idAFAttachment::PlayIdleAnim
439 void idAFAttachment::PlayIdleAnim( int blendTime ) {
440 if ( idleAnim && ( idleAnim != animator.CurrentAnim( ANIMCHANNEL_ALL )->AnimNum() ) ) {
441 animator.CycleAnim( ANIMCHANNEL_ALL, idleAnim, gameLocal.time, blendTime );
447 idAfAttachment::Think
450 void idAFAttachment::Think( void ) {
451 idAnimatedEntity::Think();
452 if ( thinkFlags & TH_UPDATEPARTICLES ) {
453 UpdateDamageEffects();
459 idAFAttachment::SetCombatModel
462 void idAFAttachment::SetCombatModel( void ) {
464 combatModel->Unlink();
465 combatModel->LoadModel( modelDefHandle );
467 combatModel = new idClipModel( modelDefHandle );
469 combatModel->SetOwner( body );
474 idAFAttachment::GetCombatModel
477 idClipModel *idAFAttachment::GetCombatModel( void ) const {
483 idAFAttachment::LinkCombat
486 void idAFAttachment::LinkCombat( void ) {
492 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
498 idAFAttachment::UnlinkCombat
501 void idAFAttachment::UnlinkCombat( void ) {
503 combatModel->Unlink();
509 ===============================================================================
513 ===============================================================================
516 const idEventDef EV_SetConstraintPosition( "SetConstraintPosition", "sv" );
518 CLASS_DECLARATION( idAnimatedEntity, idAFEntity_Base )
519 EVENT( EV_SetConstraintPosition, idAFEntity_Base::Event_SetConstraintPosition )
522 static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f;
523 static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
527 idAFEntity_Base::idAFEntity_Base
530 idAFEntity_Base::idAFEntity_Base( void ) {
532 combatModelContents = 0;
535 spawnAxis.Identity();
540 idAFEntity_Base::~idAFEntity_Base
543 idAFEntity_Base::~idAFEntity_Base( void ) {
550 idAFEntity_Base::Save
553 void idAFEntity_Base::Save( idSaveGame *savefile ) const {
554 savefile->WriteInt( combatModelContents );
555 savefile->WriteClipModel( combatModel );
556 savefile->WriteVec3( spawnOrigin );
557 savefile->WriteMat3( spawnAxis );
558 savefile->WriteInt( nextSoundTime );
564 idAFEntity_Base::Restore
567 void idAFEntity_Base::Restore( idRestoreGame *savefile ) {
568 savefile->ReadInt( combatModelContents );
569 savefile->ReadClipModel( combatModel );
570 savefile->ReadVec3( spawnOrigin );
571 savefile->ReadMat3( spawnAxis );
572 savefile->ReadInt( nextSoundTime );
575 af.Restore( savefile );
580 idAFEntity_Base::Spawn
583 void idAFEntity_Base::Spawn( void ) {
584 spawnOrigin = GetPhysics()->GetOrigin();
585 spawnAxis = GetPhysics()->GetAxis();
591 idAFEntity_Base::LoadAF
594 bool idAFEntity_Base::LoadAF( void ) {
597 if ( !spawnArgs.GetString( "articulatedFigure", "*unknown*", fileName ) ) {
601 af.SetAnimator( GetAnimator() );
602 if ( !af.Load( this, fileName ) ) {
603 gameLocal.Error( "idAFEntity_Base::LoadAF: Couldn't load af file '%s' on entity '%s'", fileName.c_str(), name.c_str() );
608 af.GetPhysics()->Rotate( spawnAxis.ToRotation() );
609 af.GetPhysics()->Translate( spawnOrigin );
611 LoadState( spawnArgs );
613 af.UpdateAnimation();
614 animator.CreateFrame( gameLocal.time, true );
622 idAFEntity_Base::Think
625 void idAFEntity_Base::Think( void ) {
628 if ( thinkFlags & TH_UPDATEVISUALS ) {
636 idAFEntity_Base::BodyForClipModelId
639 int idAFEntity_Base::BodyForClipModelId( int id ) const {
640 return af.BodyForClipModelId( id );
645 idAFEntity_Base::SaveState
648 void idAFEntity_Base::SaveState( idDict &args ) const {
649 const idKeyValue *kv;
651 // save the ragdoll pose
652 af.SaveState( args );
654 // save all the bind constraints
655 kv = spawnArgs.MatchPrefix( "bindConstraint ", NULL );
657 args.Set( kv->GetKey(), kv->GetValue() );
658 kv = spawnArgs.MatchPrefix( "bindConstraint ", kv );
661 // save the bind if it exists
662 kv = spawnArgs.FindKey( "bind" );
664 args.Set( kv->GetKey(), kv->GetValue() );
666 kv = spawnArgs.FindKey( "bindToJoint" );
668 args.Set( kv->GetKey(), kv->GetValue() );
670 kv = spawnArgs.FindKey( "bindToBody" );
672 args.Set( kv->GetKey(), kv->GetValue() );
678 idAFEntity_Base::LoadState
681 void idAFEntity_Base::LoadState( const idDict &args ) {
682 af.LoadState( args );
687 idAFEntity_Base::AddBindConstraints
690 void idAFEntity_Base::AddBindConstraints( void ) {
691 af.AddBindConstraints();
696 idAFEntity_Base::RemoveBindConstraints
699 void idAFEntity_Base::RemoveBindConstraints( void ) {
700 af.RemoveBindConstraints();
705 idAFEntity_Base::GetImpactInfo
708 void idAFEntity_Base::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
709 if ( af.IsActive() ) {
710 af.GetImpactInfo( ent, id, point, info );
712 idEntity::GetImpactInfo( ent, id, point, info );
718 idAFEntity_Base::ApplyImpulse
721 void idAFEntity_Base::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
722 if ( af.IsLoaded() ) {
723 af.ApplyImpulse( ent, id, point, impulse );
725 if ( !af.IsActive() ) {
726 idEntity::ApplyImpulse( ent, id, point, impulse );
732 idAFEntity_Base::AddForce
735 void idAFEntity_Base::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
736 if ( af.IsLoaded() ) {
737 af.AddForce( ent, id, point, force );
739 if ( !af.IsActive() ) {
740 idEntity::AddForce( ent, id, point, force );
746 idAFEntity_Base::Collide
749 bool idAFEntity_Base::Collide( const trace_t &collision, const idVec3 &velocity ) {
752 if ( af.IsActive() ) {
753 v = -( velocity * collision.c.normal );
754 if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
755 f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) );
756 if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) {
757 // don't set the volume unless there is a bounce sound as it overrides the entire channel
758 // which causes footsteps on ai's to not honor their shader parms
761 nextSoundTime = gameLocal.time + 500;
770 idAFEntity_Base::GetPhysicsToVisualTransform
773 bool idAFEntity_Base::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
774 if ( af.IsActive() ) {
775 af.GetPhysicsToVisualTransform( origin, axis );
778 return idEntity::GetPhysicsToVisualTransform( origin, axis );
783 idAFEntity_Base::UpdateAnimationControllers
786 bool idAFEntity_Base::UpdateAnimationControllers( void ) {
787 if ( af.IsActive() ) {
788 if ( af.UpdateAnimation() ) {
797 idAFEntity_Base::SetCombatModel
800 void idAFEntity_Base::SetCombatModel( void ) {
802 combatModel->Unlink();
803 combatModel->LoadModel( modelDefHandle );
805 combatModel = new idClipModel( modelDefHandle );
811 idAFEntity_Base::GetCombatModel
814 idClipModel *idAFEntity_Base::GetCombatModel( void ) const {
820 idAFEntity_Base::SetCombatContents
823 void idAFEntity_Base::SetCombatContents( bool enable ) {
824 assert( combatModel );
825 if ( enable && combatModelContents ) {
826 assert( !combatModel->GetContents() );
827 combatModel->SetContents( combatModelContents );
828 combatModelContents = 0;
829 } else if ( !enable && combatModel->GetContents() ) {
830 assert( !combatModelContents );
831 combatModelContents = combatModel->GetContents();
832 combatModel->SetContents( 0 );
838 idAFEntity_Base::LinkCombat
841 void idAFEntity_Base::LinkCombat( void ) {
846 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
852 idAFEntity_Base::UnlinkCombat
855 void idAFEntity_Base::UnlinkCombat( void ) {
857 combatModel->Unlink();
863 idAFEntity_Base::FreeModelDef
866 void idAFEntity_Base::FreeModelDef( void ) {
868 idEntity::FreeModelDef();
873 idAFEntity_Base::ShowEditingDialog
876 void idAFEntity_Base::ShowEditingDialog( void ) {
877 common->InitTool( EDITOR_AF, &spawnArgs );
882 idAFEntity_Base::DropAFs
884 The entity should have the following key/value pairs set:
885 "def_drop<type>AF" "af def"
886 "drop<type>Skin" "skin name"
887 To drop multiple articulated figures the following key/value pairs can be used:
888 "def_drop<type>AF*" "af def"
889 where * is an aribtrary string.
892 void idAFEntity_Base::DropAFs( idEntity *ent, const char *type, idList<idEntity *> *list ) {
893 const idKeyValue *kv;
894 const char *skinName;
898 const idDeclSkin *skin;
900 // drop the articulated figures
901 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), NULL );
904 args.Set( "classname", kv->GetValue() );
905 gameLocal.SpawnEntityDef( args, &newEnt );
907 if ( newEnt && newEnt->IsType( idAFEntity_Base::Type ) ) {
908 af = static_cast<idAFEntity_Base *>(newEnt);
909 af->GetPhysics()->SetOrigin( ent->GetPhysics()->GetOrigin() );
910 af->GetPhysics()->SetAxis( ent->GetPhysics()->GetAxis() );
911 af->af.SetupPose( ent, gameLocal.time );
917 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), kv );
920 // change the skin to hide all the dropped articulated figures
921 skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) );
923 skin = declManager->FindSkin( skinName );
924 ent->SetSkin( skin );
930 idAFEntity_Base::Event_SetConstraintPosition
933 void idAFEntity_Base::Event_SetConstraintPosition( const char *name, const idVec3 &pos ) {
934 af.SetConstraintPosition( name, pos );
938 ===============================================================================
942 ===============================================================================
945 const idEventDef EV_Gib( "gib", "s" );
946 const idEventDef EV_Gibbed( "<gibbed>" );
948 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Gibbable )
949 EVENT( EV_Gib, idAFEntity_Gibbable::Event_Gib )
950 EVENT( EV_Gibbed, idAFEntity_Base::Event_Remove )
956 idAFEntity_Gibbable::idAFEntity_Gibbable
959 idAFEntity_Gibbable::idAFEntity_Gibbable( void ) {
960 skeletonModel = NULL;
961 skeletonModelDefHandle = -1;
970 idAFEntity_Gibbable::~idAFEntity_Gibbable
973 idAFEntity_Gibbable::~idAFEntity_Gibbable() {
974 if ( skeletonModelDefHandle != -1 ) {
975 gameRenderWorld->FreeEntityDef( skeletonModelDefHandle );
976 skeletonModelDefHandle = -1;
982 idAFEntity_Gibbable::Save
985 void idAFEntity_Gibbable::Save( idSaveGame *savefile ) const {
986 savefile->WriteBool( gibbed );
987 savefile->WriteBool( combatModel != NULL );
989 savefile->WriteBool( wasThrown );
995 idAFEntity_Gibbable::Restore
998 void idAFEntity_Gibbable::Restore( idRestoreGame *savefile ) {
1001 savefile->ReadBool( gibbed );
1002 savefile->ReadBool( hasCombatModel );
1004 savefile->ReadBool( wasThrown );
1007 InitSkeletonModel();
1009 if ( hasCombatModel ) {
1017 idAFEntity_Gibbable::Spawn
1020 void idAFEntity_Gibbable::Spawn( void ) {
1021 InitSkeletonModel();
1031 idAFEntity_Gibbable::InitSkeletonModel
1034 void idAFEntity_Gibbable::InitSkeletonModel( void ) {
1035 const char *modelName;
1036 const idDeclModelDef *modelDef;
1038 skeletonModel = NULL;
1039 skeletonModelDefHandle = -1;
1041 modelName = spawnArgs.GetString( "model_gib" );
1044 if ( modelName[0] != '\0' ) {
1045 modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
1047 skeletonModel = modelDef->ModelHandle();
1049 skeletonModel = renderModelManager->FindModel( modelName );
1051 if ( skeletonModel != NULL && renderEntity.hModel != NULL ) {
1052 if ( skeletonModel->NumJoints() != renderEntity.hModel->NumJoints() ) {
1053 gameLocal.Error( "gib model '%s' has different number of joints than model '%s'",
1054 skeletonModel->Name(), renderEntity.hModel->Name() );
1062 idAFEntity_Gibbable::Present
1065 void idAFEntity_Gibbable::Present( void ) {
1066 renderEntity_t skeleton;
1068 if ( !gameLocal.isNewFrame ) {
1072 // don't present to the renderer if the entity hasn't changed
1073 if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
1077 // update skeleton model
1078 if ( gibbed && !IsHidden() && skeletonModel != NULL ) {
1079 skeleton = renderEntity;
1080 skeleton.hModel = skeletonModel;
1081 // add to refresh list
1082 if ( skeletonModelDefHandle == -1 ) {
1083 skeletonModelDefHandle = gameRenderWorld->AddEntityDef( &skeleton );
1085 gameRenderWorld->UpdateEntityDef( skeletonModelDefHandle, &skeleton );
1089 idEntity::Present();
1094 idAFEntity_Gibbable::Damage
1097 void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
1098 if ( !fl.takedamage ) {
1101 idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
1102 if ( health < -20 && spawnArgs.GetBool( "gib" ) ) {
1103 Gib( dir, damageDefName );
1109 =====================
1110 idAFEntity_Gibbable::SetThrown
1111 =====================
1113 void idAFEntity_Gibbable::SetThrown( bool isThrown ) {
1116 int i, num = af.GetPhysics()->GetNumBodies();
1118 for ( i=0; i<num; i++ ) {
1121 body = af.GetPhysics()->GetBody( i );
1122 body->SetClipMask( MASK_MONSTERSOLID );
1126 wasThrown = isThrown;
1130 =====================
1131 idAFEntity_Gibbable::Collide
1132 =====================
1134 bool idAFEntity_Gibbable::Collide( const trace_t &collision, const idVec3 &velocity ) {
1136 if ( !gibbed && wasThrown ) {
1138 // Everything gibs (if possible)
1139 if ( spawnArgs.GetBool( "gib" ) ) {
1142 ent = gameLocal.entities[ collision.c.entityNum ];
1143 if ( ent->fl.takedamage ) {
1144 ent->Damage( this, gameLocal.GetLocalPlayer(), collision.c.normal, "damage_thrown_ragdoll", 1.f, CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) );
1147 idVec3 vel = velocity;
1148 vel.NormalizeFast();
1149 Gib( vel, "damage_gib" );
1153 return idAFEntity_Base::Collide( collision, velocity );
1158 =====================
1159 idAFEntity_Gibbable::SpawnGibs
1160 =====================
1162 void idAFEntity_Gibbable::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
1165 idVec3 entityCenter, velocity;
1166 idList<idEntity *> list;
1168 assert( !gameLocal.isClient );
1170 const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1172 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1175 // spawn gib articulated figures
1176 idAFEntity_Base::DropAFs( this, "gib", &list );
1179 idMoveableItem::DropItems( this, "gib", &list );
1181 // blow out the gibs in the given direction away from the center of the entity
1182 entityCenter = GetPhysics()->GetAbsBounds().GetCenter();
1183 gibNonSolid = damageDef->GetBool( "gibNonSolid" );
1184 for ( i = 0; i < list.Num(); i++ ) {
1185 if ( gibNonSolid ) {
1186 list[i]->GetPhysics()->SetContents( 0 );
1187 list[i]->GetPhysics()->SetClipMask( 0 );
1188 list[i]->GetPhysics()->UnlinkClip();
1189 list[i]->GetPhysics()->PutToRest();
1192 list[i]->GetPhysics()->SetContents( 0 );
1194 list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE );
1196 list[i]->GetPhysics()->SetClipMask( CONTENTS_SOLID );
1197 velocity = list[i]->GetPhysics()->GetAbsBounds().GetCenter() - entityCenter;
1198 velocity.NormalizeFast();
1199 velocity += ( i & 1 ) ? dir : -dir;
1200 list[i]->GetPhysics()->SetLinearVelocity( velocity * 75.0f );
1203 // Don't allow grabber to pick up temporary gibs
1204 list[i]->noGrab = true;
1206 list[i]->GetRenderEntity()->noShadow = true;
1207 list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1208 list[i]->PostEventSec( &EV_Remove, 4.0f );
1214 idAFEntity_Gibbable::Gib
1217 void idAFEntity_Gibbable::Gib( const idVec3 &dir, const char *damageDefName ) {
1224 // Don't grab this ent after it's been gibbed (and now invisible!)
1228 const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1230 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1233 if ( damageDef->GetBool( "gibNonSolid" ) ) {
1234 GetAFPhysics()->SetContents( 0 );
1235 GetAFPhysics()->SetClipMask( 0 );
1236 GetAFPhysics()->UnlinkClip();
1237 GetAFPhysics()->PutToRest();
1239 GetAFPhysics()->SetContents( CONTENTS_CORPSE );
1240 GetAFPhysics()->SetClipMask( CONTENTS_SOLID );
1245 if ( g_bloodEffects.GetBool() ) {
1246 if ( gameLocal.time > gameLocal.GetGibTime() ) {
1247 gameLocal.SetGibTime( gameLocal.time + GIB_DELAY );
1248 SpawnGibs( dir, damageDefName );
1249 renderEntity.noShadow = true;
1250 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1251 StartSound( "snd_gibbed", SND_CHANNEL_ANY, 0, false, NULL );
1259 PostEventSec( &EV_Gibbed, 4.0f );
1264 idAFEntity_Gibbable::Event_Gib
1267 void idAFEntity_Gibbable::Event_Gib( const char *damageDefName ) {
1268 Gib( idVec3( 0, 0, 1 ), damageDefName );
1272 ===============================================================================
1276 ===============================================================================
1279 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_Generic )
1280 EVENT( EV_Activate, idAFEntity_Generic::Event_Activate )
1285 idAFEntity_Generic::idAFEntity_Generic
1288 idAFEntity_Generic::idAFEntity_Generic( void ) {
1289 keepRunningPhysics = false;
1294 idAFEntity_Generic::~idAFEntity_Generic
1297 idAFEntity_Generic::~idAFEntity_Generic( void ) {
1302 idAFEntity_Generic::Save
1305 void idAFEntity_Generic::Save( idSaveGame *savefile ) const {
1306 savefile->WriteBool( keepRunningPhysics );
1311 idAFEntity_Generic::Restore
1314 void idAFEntity_Generic::Restore( idRestoreGame *savefile ) {
1315 savefile->ReadBool( keepRunningPhysics );
1320 idAFEntity_Generic::Think
1323 void idAFEntity_Generic::Think( void ) {
1324 idAFEntity_Base::Think();
1326 if ( keepRunningPhysics ) {
1327 BecomeActive( TH_PHYSICS );
1333 idAFEntity_Generic::Spawn
1336 void idAFEntity_Generic::Spawn( void ) {
1338 gameLocal.Error( "Couldn't load af file on entity '%s'", name.c_str() );
1343 SetPhysics( af.GetPhysics() );
1345 af.GetPhysics()->PutToRest();
1346 if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1347 af.GetPhysics()->Activate();
1350 fl.takedamage = true;
1355 idAFEntity_Generic::Event_Activate
1358 void idAFEntity_Generic::Event_Activate( idEntity *activator ) {
1360 idVec3 init_velocity, init_avelocity;
1364 af.GetPhysics()->EnableImpact();
1365 af.GetPhysics()->Activate();
1367 spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1368 spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1370 delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1371 if ( delay == 0.0f ) {
1372 af.GetPhysics()->SetLinearVelocity( init_velocity );
1374 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1377 delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1378 if ( delay == 0.0f ) {
1379 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1381 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1387 ===============================================================================
1389 idAFEntity_WithAttachedHead
1391 ===============================================================================
1394 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_WithAttachedHead )
1395 EVENT( EV_Gib, idAFEntity_WithAttachedHead::Event_Gib )
1396 EVENT( EV_Activate, idAFEntity_WithAttachedHead::Event_Activate )
1401 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead
1404 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead() {
1410 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead
1413 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead() {
1414 if ( head.GetEntity() ) {
1415 head.GetEntity()->ClearBody();
1416 head.GetEntity()->PostEventMS( &EV_Remove, 0 );
1422 idAFEntity_WithAttachedHead::Spawn
1425 void idAFEntity_WithAttachedHead::Spawn( void ) {
1432 SetPhysics( af.GetPhysics() );
1434 af.GetPhysics()->PutToRest();
1435 if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1436 af.GetPhysics()->Activate();
1439 fl.takedamage = true;
1441 if ( head.GetEntity() ) {
1442 int anim = head.GetEntity()->GetAnimator()->GetAnim( "dead" );
1445 head.GetEntity()->GetAnimator()->SetFrame( ANIMCHANNEL_ALL, anim, 0, gameLocal.time, 0 );
1452 idAFEntity_WithAttachedHead::Save
1455 void idAFEntity_WithAttachedHead::Save( idSaveGame *savefile ) const {
1456 head.Save( savefile );
1461 idAFEntity_WithAttachedHead::Restore
1464 void idAFEntity_WithAttachedHead::Restore( idRestoreGame *savefile ) {
1465 head.Restore( savefile );
1470 idAFEntity_WithAttachedHead::SetupHead
1473 void idAFEntity_WithAttachedHead::SetupHead( void ) {
1474 idAFAttachment *headEnt;
1476 const char *headModel;
1477 jointHandle_t joint;
1481 headModel = spawnArgs.GetString( "def_head", "" );
1482 if ( headModel[ 0 ] ) {
1483 jointName = spawnArgs.GetString( "head_joint" );
1484 joint = animator.GetJointHandle( jointName );
1485 if ( joint == INVALID_JOINT ) {
1486 gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
1489 headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, NULL ) );
1490 headEnt->SetName( va( "%s_head", name.c_str() ) );
1491 headEnt->SetBody( this, headModel, joint );
1492 headEnt->SetCombatModel();
1497 if ( spawnArgs.GetString( "skin_head_xray", "", xSkin ) ) {
1498 headEnt->xraySkin = declManager->FindSkin( xSkin.c_str() );
1499 headEnt->UpdateModel();
1502 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
1503 origin = renderEntity.origin + origin * renderEntity.axis;
1504 headEnt->SetOrigin( origin );
1505 headEnt->SetAxis( renderEntity.axis );
1506 headEnt->BindToJoint( this, joint, true );
1512 idAFEntity_WithAttachedHead::Think
1515 void idAFEntity_WithAttachedHead::Think( void ) {
1516 idAFEntity_Base::Think();
1521 idAFEntity_WithAttachedHead::LinkCombat
1524 void idAFEntity_WithAttachedHead::LinkCombat( void ) {
1525 idAFAttachment *headEnt;
1531 if ( combatModel ) {
1532 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1534 headEnt = head.GetEntity();
1536 headEnt->LinkCombat();
1542 idAFEntity_WithAttachedHead::UnlinkCombat
1545 void idAFEntity_WithAttachedHead::UnlinkCombat( void ) {
1546 idAFAttachment *headEnt;
1548 if ( combatModel ) {
1549 combatModel->Unlink();
1551 headEnt = head.GetEntity();
1553 headEnt->UnlinkCombat();
1559 idAFEntity_WithAttachedHead::Hide
1562 void idAFEntity_WithAttachedHead::Hide( void ) {
1563 idAFEntity_Base::Hide();
1564 if ( head.GetEntity() ) {
1565 head.GetEntity()->Hide();
1572 idAFEntity_WithAttachedHead::Show
1575 void idAFEntity_WithAttachedHead::Show( void ) {
1576 idAFEntity_Base::Show();
1577 if ( head.GetEntity() ) {
1578 head.GetEntity()->Show();
1585 idAFEntity_WithAttachedHead::ProjectOverlay
1588 void idAFEntity_WithAttachedHead::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1590 idEntity::ProjectOverlay( origin, dir, size, material );
1592 if ( head.GetEntity() ) {
1593 head.GetEntity()->ProjectOverlay( origin, dir, size, material );
1599 idAFEntity_WithAttachedHead::Gib
1602 void idAFEntity_WithAttachedHead::Gib( const idVec3 &dir, const char *damageDefName ) {
1607 idAFEntity_Gibbable::Gib( dir, damageDefName );
1608 if ( head.GetEntity() ) {
1609 head.GetEntity()->Hide();
1615 idAFEntity_WithAttachedHead::Event_Gib
1618 void idAFEntity_WithAttachedHead::Event_Gib( const char *damageDefName ) {
1619 Gib( idVec3( 0, 0, 1 ), damageDefName );
1624 idAFEntity_WithAttachedHead::Event_Activate
1627 void idAFEntity_WithAttachedHead::Event_Activate( idEntity *activator ) {
1629 idVec3 init_velocity, init_avelocity;
1633 af.GetPhysics()->EnableImpact();
1634 af.GetPhysics()->Activate();
1636 spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1637 spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1639 delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1640 if ( delay == 0.0f ) {
1641 af.GetPhysics()->SetLinearVelocity( init_velocity );
1643 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1646 delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1647 if ( delay == 0.0f ) {
1648 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1650 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1656 ===============================================================================
1660 ===============================================================================
1663 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Vehicle )
1668 idAFEntity_Vehicle::idAFEntity_Vehicle
1671 idAFEntity_Vehicle::idAFEntity_Vehicle( void ) {
1673 eyesJoint = INVALID_JOINT;
1674 steeringWheelJoint = INVALID_JOINT;
1683 idAFEntity_Vehicle::Spawn
1686 void idAFEntity_Vehicle::Spawn( void ) {
1687 const char *eyesJointName = spawnArgs.GetString( "eyesJoint", "eyes" );
1688 const char *steeringWheelJointName = spawnArgs.GetString( "steeringWheelJoint", "steeringWheel" );
1694 SetPhysics( af.GetPhysics() );
1696 fl.takedamage = true;
1698 if ( !eyesJointName[0] ) {
1699 gameLocal.Error( "idAFEntity_Vehicle '%s' no eyes joint specified", name.c_str() );
1701 eyesJoint = animator.GetJointHandle( eyesJointName );
1702 if ( !steeringWheelJointName[0] ) {
1703 gameLocal.Error( "idAFEntity_Vehicle '%s' no steering wheel joint specified", name.c_str() );
1705 steeringWheelJoint = animator.GetJointHandle( steeringWheelJointName );
1707 spawnArgs.GetFloat( "wheelRadius", "20", wheelRadius );
1708 spawnArgs.GetFloat( "steerSpeed", "5", steerSpeed );
1713 const char *smokeName = spawnArgs.GetString( "smoke_vehicle_dust", "muzzlesmoke" );
1714 if ( *smokeName != '\0' ) {
1715 dustSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1721 idAFEntity_Vehicle::Use
1724 void idAFEntity_Vehicle::Use( idPlayer *other ) {
1729 if ( player == other ) {
1733 af.GetPhysics()->SetComeToRest( true );
1738 animator.GetJointTransform( eyesJoint, gameLocal.time, origin, axis );
1739 origin = renderEntity.origin + origin * renderEntity.axis;
1740 player->GetPhysics()->SetOrigin( origin );
1741 player->BindToBody( this, 0, true );
1743 af.GetPhysics()->SetComeToRest( false );
1744 af.GetPhysics()->Activate();
1750 idAFEntity_Vehicle::GetSteerAngle
1753 float idAFEntity_Vehicle::GetSteerAngle( void ) {
1754 float idealSteerAngle, angleDelta;
1756 idealSteerAngle = player->usercmd.rightmove * ( 30.0f / 128.0f );
1757 angleDelta = idealSteerAngle - steerAngle;
1759 if ( angleDelta > steerSpeed ) {
1760 steerAngle += steerSpeed;
1761 } else if ( angleDelta < -steerSpeed ) {
1762 steerAngle -= steerSpeed;
1764 steerAngle = idealSteerAngle;
1772 ===============================================================================
1774 idAFEntity_VehicleSimple
1776 ===============================================================================
1779 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSimple )
1784 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple
1787 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple( void ) {
1789 for ( i = 0; i < 4; i++ ) {
1790 suspension[i] = NULL;
1796 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple
1799 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple( void ) {
1806 idAFEntity_VehicleSimple::Spawn
1809 void idAFEntity_VehicleSimple::Spawn( void ) {
1810 static const char *wheelJointKeys[] = {
1811 "wheelJointFrontLeft",
1812 "wheelJointFrontRight",
1813 "wheelJointRearLeft",
1814 "wheelJointRearRight"
1816 static idVec3 wheelPoly[4] = { idVec3( 2, 2, 0 ), idVec3( 2, -2, 0 ), idVec3( -2, -2, 0 ), idVec3( -2, 2, 0 ) };
1823 trm.SetupPolygon( wheelPoly, 4 );
1824 trm.Translate( idVec3( 0, 0, -wheelRadius ) );
1825 wheelModel = new idClipModel( trm );
1827 for ( i = 0; i < 4; i++ ) {
1828 const char *wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
1829 if ( !wheelJointName[0] ) {
1830 gameLocal.Error( "idAFEntity_VehicleSimple '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
1832 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
1833 if ( wheelJoints[i] == INVALID_JOINT ) {
1834 gameLocal.Error( "idAFEntity_VehicleSimple '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
1837 GetAnimator()->GetJointTransform( wheelJoints[i], 0, origin, axis );
1838 origin = renderEntity.origin + origin * renderEntity.axis;
1840 suspension[i] = new idAFConstraint_Suspension();
1841 suspension[i]->Setup( va( "suspension%d", i ), af.GetPhysics()->GetBody( 0 ), origin, af.GetPhysics()->GetAxis( 0 ), wheelModel );
1842 suspension[i]->SetSuspension( g_vehicleSuspensionUp.GetFloat(),
1843 g_vehicleSuspensionDown.GetFloat(),
1844 g_vehicleSuspensionKCompress.GetFloat(),
1845 g_vehicleSuspensionDamping.GetFloat(),
1846 g_vehicleTireFriction.GetFloat() );
1848 af.GetPhysics()->AddConstraint( suspension[i] );
1851 memset( wheelAngles, 0, sizeof( wheelAngles ) );
1852 BecomeActive( TH_THINK );
1857 idAFEntity_VehicleSimple::Think
1860 void idAFEntity_VehicleSimple::Think( void ) {
1862 float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
1865 idRotation wheelRotation, steerRotation;
1867 if ( thinkFlags & TH_THINK ) {
1870 // capture the input from a player
1871 velocity = g_vehicleVelocity.GetFloat();
1872 if ( player->usercmd.forwardmove < 0 ) {
1873 velocity = -velocity;
1875 force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
1876 steerAngle = GetSteerAngle();
1879 // update the wheel motor force and steering
1880 for ( i = 0; i < 2; i++ ) {
1882 // front wheel drive
1883 if ( velocity != 0.0f ) {
1884 suspension[i]->EnableMotor( true );
1886 suspension[i]->EnableMotor( false );
1888 suspension[i]->SetMotorVelocity( velocity );
1889 suspension[i]->SetMotorForce( force );
1891 // update the wheel steering
1892 suspension[i]->SetSteerAngle( steerAngle );
1895 // adjust wheel velocity for better steering because there are no differentials between the wheels
1896 if ( steerAngle < 0.0f ) {
1897 suspension[0]->SetMotorVelocity( velocity * 0.5f );
1898 } else if ( steerAngle > 0.0f ) {
1899 suspension[1]->SetMotorVelocity( velocity * 0.5f );
1902 // update suspension with latest cvar settings
1903 for ( i = 0; i < 4; i++ ) {
1904 suspension[i]->SetSuspension( g_vehicleSuspensionUp.GetFloat(),
1905 g_vehicleSuspensionDown.GetFloat(),
1906 g_vehicleSuspensionKCompress.GetFloat(),
1907 g_vehicleSuspensionDamping.GetFloat(),
1908 g_vehicleTireFriction.GetFloat() );
1914 // move and rotate the wheels visually
1915 for ( i = 0; i < 4; i++ ) {
1916 idAFBody *body = af.GetPhysics()->GetBody( 0 );
1918 origin = suspension[i]->GetWheelOrigin();
1919 velocity = body->GetPointVelocity( origin ) * body->GetWorldAxis()[0];
1920 wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
1922 // additional rotation about the wheel axis
1923 wheelRotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
1924 wheelRotation.SetVec( 0, -1, 0 );
1927 // rotate the wheel for steering
1928 steerRotation.SetAngle( steerAngle );
1929 steerRotation.SetVec( 0, 0, 1 );
1930 // set wheel rotation
1931 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() * steerRotation.ToMat3() );
1933 // set wheel rotation
1934 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() );
1937 // set wheel position for suspension
1938 origin = ( origin - renderEntity.origin ) * renderEntity.axis.Transpose();
1939 GetAnimator()->SetJointPos( wheelJoints[i], JOINTMOD_WORLD_OVERRIDE, origin );
1942 // spawn dust particle effects
1943 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
1945 idAFConstraint_Contact *contacts[2];
1946 for ( i = 0; i < 4; i++ ) {
1947 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
1948 for ( int j = 0; j < numContacts; j++ ) {
1949 gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
1957 if ( thinkFlags & TH_UPDATEVISUALS ) {
1965 ===============================================================================
1967 idAFEntity_VehicleFourWheels
1969 ===============================================================================
1972 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleFourWheels )
1978 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels
1981 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels( void ) {
1984 for ( i = 0; i < 4; i++ ) {
1986 wheelJoints[i] = INVALID_JOINT;
1987 wheelAngles[i] = 0.0f;
1995 idAFEntity_VehicleFourWheels::Spawn
1998 void idAFEntity_VehicleFourWheels::Spawn( void ) {
2000 static const char *wheelBodyKeys[] = {
2001 "wheelBodyFrontLeft",
2002 "wheelBodyFrontRight",
2003 "wheelBodyRearLeft",
2004 "wheelBodyRearRight"
2006 static const char *wheelJointKeys[] = {
2007 "wheelJointFrontLeft",
2008 "wheelJointFrontRight",
2009 "wheelJointRearLeft",
2010 "wheelJointRearRight"
2012 static const char *steeringHingeKeys[] = {
2013 "steeringHingeFrontLeft",
2014 "steeringHingeFrontRight",
2017 const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2019 for ( i = 0; i < 4; i++ ) {
2020 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2021 if ( !wheelBodyName[0] ) {
2022 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2024 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2026 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2028 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2029 if ( !wheelJointName[0] ) {
2030 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2032 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2033 if ( wheelJoints[i] == INVALID_JOINT ) {
2034 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2038 for ( i = 0; i < 2; i++ ) {
2039 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2040 if ( !steeringHingeName[0] ) {
2041 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2043 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2044 if ( !steering[i] ) {
2045 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2049 memset( wheelAngles, 0, sizeof( wheelAngles ) );
2050 BecomeActive( TH_THINK );
2055 idAFEntity_VehicleFourWheels::Think
2058 void idAFEntity_VehicleFourWheels::Think( void ) {
2060 float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2063 idRotation rotation;
2065 if ( thinkFlags & TH_THINK ) {
2068 // capture the input from a player
2069 velocity = g_vehicleVelocity.GetFloat();
2070 if ( player->usercmd.forwardmove < 0 ) {
2071 velocity = -velocity;
2073 force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2074 steerAngle = GetSteerAngle();
2077 // update the wheel motor force
2078 for ( i = 0; i < 2; i++ ) {
2079 wheels[2+i]->SetContactMotorVelocity( velocity );
2080 wheels[2+i]->SetContactMotorForce( force );
2083 // adjust wheel velocity for better steering because there are no differentials between the wheels
2084 if ( steerAngle < 0.0f ) {
2085 wheels[2]->SetContactMotorVelocity( velocity * 0.5f );
2087 else if ( steerAngle > 0.0f ) {
2088 wheels[3]->SetContactMotorVelocity( velocity * 0.5f );
2091 // update the wheel steering
2092 steering[0]->SetSteerAngle( steerAngle );
2093 steering[1]->SetSteerAngle( steerAngle );
2094 for ( i = 0; i < 2; i++ ) {
2095 steering[i]->SetSteerSpeed( 3.0f );
2098 // update the steering wheel
2099 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2100 rotation.SetVec( axis[2] );
2101 rotation.SetAngle( -steerAngle );
2102 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2107 // rotate the wheels visually
2108 for ( i = 0; i < 4; i++ ) {
2109 if ( force == 0.0f ) {
2110 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2112 wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2113 // give the wheel joint an additional rotation about the wheel axis
2114 rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2115 axis = af.GetPhysics()->GetAxis( 0 );
2116 rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2117 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2120 // spawn dust particle effects
2121 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2123 idAFConstraint_Contact *contacts[2];
2124 for ( i = 0; i < 4; i++ ) {
2125 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2126 for ( int j = 0; j < numContacts; j++ ) {
2127 gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
2134 if ( thinkFlags & TH_UPDATEVISUALS ) {
2142 ===============================================================================
2144 idAFEntity_VehicleSixWheels
2146 ===============================================================================
2149 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSixWheels )
2154 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels
2157 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels( void ) {
2160 for ( i = 0; i < 6; i++ ) {
2162 wheelJoints[i] = INVALID_JOINT;
2163 wheelAngles[i] = 0.0f;
2173 idAFEntity_VehicleSixWheels::Spawn
2176 void idAFEntity_VehicleSixWheels::Spawn( void ) {
2178 static const char *wheelBodyKeys[] = {
2179 "wheelBodyFrontLeft",
2180 "wheelBodyFrontRight",
2181 "wheelBodyMiddleLeft",
2182 "wheelBodyMiddleRight",
2183 "wheelBodyRearLeft",
2184 "wheelBodyRearRight"
2186 static const char *wheelJointKeys[] = {
2187 "wheelJointFrontLeft",
2188 "wheelJointFrontRight",
2189 "wheelJointMiddleLeft",
2190 "wheelJointMiddleRight",
2191 "wheelJointRearLeft",
2192 "wheelJointRearRight"
2194 static const char *steeringHingeKeys[] = {
2195 "steeringHingeFrontLeft",
2196 "steeringHingeFrontRight",
2197 "steeringHingeRearLeft",
2198 "steeringHingeRearRight"
2201 const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2203 for ( i = 0; i < 6; i++ ) {
2204 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2205 if ( !wheelBodyName[0] ) {
2206 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2208 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2210 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2212 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2213 if ( !wheelJointName[0] ) {
2214 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2216 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2217 if ( wheelJoints[i] == INVALID_JOINT ) {
2218 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2222 for ( i = 0; i < 4; i++ ) {
2223 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2224 if ( !steeringHingeName[0] ) {
2225 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2227 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2228 if ( !steering[i] ) {
2229 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2233 memset( wheelAngles, 0, sizeof( wheelAngles ) );
2234 BecomeActive( TH_THINK );
2239 idAFEntity_VehicleSixWheels::Think
2242 void idAFEntity_VehicleSixWheels::Think( void ) {
2245 float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2249 idRotation rotation;
2251 if ( thinkFlags & TH_THINK ) {
2254 // capture the input from a player
2255 velocity = g_vehicleVelocity.GetFloat();
2256 if ( player->usercmd.forwardmove < 0 ) {
2257 velocity = -velocity;
2259 force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2260 steerAngle = GetSteerAngle();
2263 // update the wheel motor force
2264 for ( i = 0; i < 6; i++ ) {
2265 wheels[i]->SetContactMotorVelocity( velocity );
2266 wheels[i]->SetContactMotorForce( force );
2269 // adjust wheel velocity for better steering because there are no differentials between the wheels
2270 if ( steerAngle < 0.0f ) {
2271 for ( i = 0; i < 3; i++ ) {
2272 wheels[(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2275 else if ( steerAngle > 0.0f ) {
2276 for ( i = 0; i < 3; i++ ) {
2277 wheels[1+(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2281 // update the wheel steering
2282 steering[0]->SetSteerAngle( steerAngle );
2283 steering[1]->SetSteerAngle( steerAngle );
2284 steering[2]->SetSteerAngle( -steerAngle );
2285 steering[3]->SetSteerAngle( -steerAngle );
2286 for ( i = 0; i < 4; i++ ) {
2287 steering[i]->SetSteerSpeed( 3.0f );
2290 // update the steering wheel
2291 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2292 rotation.SetVec( axis[2] );
2293 rotation.SetAngle( -steerAngle );
2294 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2299 // rotate the wheels visually
2300 for ( i = 0; i < 6; i++ ) {
2301 if ( force == 0.0f ) {
2302 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2304 wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2305 // give the wheel joint an additional rotation about the wheel axis
2306 rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2307 axis = af.GetPhysics()->GetAxis( 0 );
2308 rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2309 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2312 // spawn dust particle effects
2313 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2315 idAFConstraint_Contact *contacts[2];
2316 for ( i = 0; i < 6; i++ ) {
2317 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2318 for ( int j = 0; j < numContacts; j++ ) {
2319 gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
2326 if ( thinkFlags & TH_UPDATEVISUALS ) {
2334 ===============================================================================
2336 idAFEntity_VehicleAutomated
2338 ===============================================================================
2340 const idEventDef EV_Vehicle_setVelocity( "setVelocity", "f" );
2341 const idEventDef EV_Vehicle_setTorque( "setTorque", "f" );
2342 const idEventDef EV_Vehicle_setSteeringSpeed( "setSteeringSpeed", "f" );
2343 const idEventDef EV_Vehicle_setWaypoint( "setWaypoint", "e" );
2345 CLASS_DECLARATION( idAFEntity_VehicleSixWheels, idAFEntity_VehicleAutomated )
2346 EVENT( EV_PostSpawn, idAFEntity_VehicleAutomated::PostSpawn )
2347 EVENT( EV_Vehicle_setVelocity, idAFEntity_VehicleAutomated::Event_SetVelocity )
2348 EVENT( EV_Vehicle_setTorque, idAFEntity_VehicleAutomated::Event_SetTorque )
2349 EVENT( EV_Vehicle_setSteeringSpeed, idAFEntity_VehicleAutomated::Event_SetSteeringSpeed )
2350 EVENT( EV_Vehicle_setWaypoint, idAFEntity_VehicleAutomated::Event_SetWayPoint )
2355 idAFEntity_VehicleAutomated::Spawn
2358 void idAFEntity_VehicleAutomated::Spawn( void ) {
2360 velocity = force = steerAngle = 0.f;
2361 currentSteering = steeringSpeed = 0.f;
2365 spawnArgs.GetFloat( "velocity", "150", velocity );
2366 spawnArgs.GetFloat( "torque", "200000", force );
2367 spawnArgs.GetFloat( "steeringSpeed", "1", steeringSpeed );
2368 spawnArgs.GetFloat( "originHeight", "0", originHeight );
2370 PostEventMS( &EV_PostSpawn, 0 );
2375 idAFEntity_VehicleAutomated::PostSpawn
2378 void idAFEntity_VehicleAutomated::PostSpawn( void ) {
2380 if ( targets.Num() ) {
2381 waypoint = targets[0].GetEntity();
2387 idAFEntity_VehicleAutomated::Event_SetVelocity
2390 void idAFEntity_VehicleAutomated::Event_SetVelocity( float _velocity ) {
2391 velocity = _velocity;
2396 idAFEntity_VehicleAutomated::Event_SetTorque
2399 void idAFEntity_VehicleAutomated::Event_SetTorque( float _torque ) {
2405 idAFEntity_VehicleAutomated::Event_SetSteeringSpeed
2408 void idAFEntity_VehicleAutomated::Event_SetSteeringSpeed( float _steeringSpeed ) {
2409 steeringSpeed = _steeringSpeed;
2414 idAFEntity_VehicleAutomated::Event_SetWayPoint
2417 void idAFEntity_VehicleAutomated::Event_SetWayPoint( idEntity *_waypoint ) {
2418 waypoint = _waypoint;
2423 idAFEntity_VehicleAutomated::Think
2426 #define HIT_WAYPOINT_THRESHOLD 80.f
2428 void idAFEntity_VehicleAutomated::Think( void ) {
2430 // If we don't have a waypoint, coast to a stop
2432 velocity = force = steerAngle = 0.f;
2433 idAFEntity_VehicleSixWheels::Think();
2437 idVec3 waypoint_origin, vehicle_origin;
2438 idVec3 travel_vector;
2439 float distance_from_waypoint;
2441 // Set up the vector from the vehicle origin, to the waypoint
2442 vehicle_origin = GetPhysics()->GetOrigin();
2443 vehicle_origin.z -= originHeight;
2445 waypoint_origin = waypoint->GetPhysics()->GetOrigin();
2447 travel_vector = waypoint_origin - vehicle_origin;
2448 distance_from_waypoint = travel_vector.Length();
2450 // Check if we've hit the waypoint (within a certain threshold)
2451 if ( distance_from_waypoint < HIT_WAYPOINT_THRESHOLD ) {
2453 const function_t *func;
2456 // Waypoints can call script functions
2457 waypoint->spawnArgs.GetString( "call", "", callfunc );
2458 if ( callfunc.Length() ) {
2459 func = gameLocal.program.FindFunction( callfunc );
2460 if ( func != NULL ) {
2461 thread = new idThread( func );
2462 thread->DelayedStart( 0 );
2466 // Get next waypoint
2467 if ( waypoint->targets.Num() ) {
2468 waypoint = waypoint->targets[0].GetEntity();
2473 // We are switching waypoints, adjust steering next frame
2474 idAFEntity_VehicleSixWheels::Think();
2478 idAngles vehicle_angles, travel_angles;
2480 // Get the angles we need to steer towards
2481 travel_angles = travel_vector.ToAngles().Normalize360();
2482 vehicle_angles = this->GetPhysics()->GetAxis().ToAngles().Normalize360();
2486 // Get the shortest steering angle towards the travel angles
2487 delta_yaw = vehicle_angles.yaw - travel_angles.yaw;
2488 if ( idMath::Fabs( delta_yaw ) > 180.f ) {
2489 if ( delta_yaw > 0 ) {
2490 delta_yaw = delta_yaw - 360;
2492 delta_yaw = delta_yaw + 360;
2496 // Maximum steering angle is 35 degrees
2497 delta_yaw = idMath::ClampFloat( -35.f, 35.f, delta_yaw );
2499 idealSteering = delta_yaw;
2501 // Adjust steering incrementally so it doesn't snap to the ideal angle
2502 if ( idMath::Fabs( (idealSteering - currentSteering) ) > steeringSpeed ) {
2503 if ( idealSteering > currentSteering ) {
2504 currentSteering += steeringSpeed;
2506 currentSteering -= steeringSpeed;
2509 currentSteering = idealSteering;
2513 if ( g_vehicleDebug.GetBool() ) {
2514 gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), vehicle_origin );
2515 gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), waypoint_origin );
2516 gameRenderWorld->DrawText( waypoint->name.c_str(), waypoint_origin + idVec3(0,0,16), 0.25f, colorYellow, gameLocal.GetLocalPlayer()->viewAxis );
2517 gameRenderWorld->DebugArrow( colorWhite, vehicle_origin, waypoint_origin, 12.f );
2520 // Set the final steerAngle for the vehicle
2521 steerAngle = currentSteering;
2523 idAFEntity_VehicleSixWheels::Think();
2528 ===============================================================================
2530 idAFEntity_SteamPipe
2532 ===============================================================================
2535 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_SteamPipe )
2541 idAFEntity_SteamPipe::idAFEntity_SteamPipe
2544 idAFEntity_SteamPipe::idAFEntity_SteamPipe( void ) {
2547 steamUpForce = 0.0f;
2548 steamModelDefHandle = -1;
2549 memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2554 idAFEntity_SteamPipe::~idAFEntity_SteamPipe
2557 idAFEntity_SteamPipe::~idAFEntity_SteamPipe( void ) {
2558 if ( steamModelDefHandle >= 0 ){
2559 gameRenderWorld->FreeEntityDef( steamModelDefHandle );
2565 idAFEntity_SteamPipe::Save
2568 void idAFEntity_SteamPipe::Save( idSaveGame *savefile ) const {
2573 idAFEntity_SteamPipe::Restore
2576 void idAFEntity_SteamPipe::Restore( idRestoreGame *savefile ) {
2582 idAFEntity_SteamPipe::Spawn
2585 void idAFEntity_SteamPipe::Spawn( void ) {
2587 const char *steamBodyName;
2593 SetPhysics( af.GetPhysics() );
2595 fl.takedamage = true;
2597 steamBodyName = spawnArgs.GetString( "steamBody", "" );
2598 steamForce = spawnArgs.GetFloat( "steamForce", "2000" );
2599 steamUpForce = spawnArgs.GetFloat( "steamUpForce", "10" );
2600 steamDir = af.GetPhysics()->GetAxis( steamBody )[2];
2601 steamBody = af.GetPhysics()->GetBodyId( steamBodyName );
2602 force.SetPosition( af.GetPhysics(), steamBody, af.GetPhysics()->GetOrigin( steamBody ) );
2603 force.SetForce( steamDir * -steamForce );
2605 InitSteamRenderEntity();
2607 BecomeActive( TH_THINK );
2612 idAFEntity_SteamPipe::InitSteamRenderEntity
2615 void idAFEntity_SteamPipe::InitSteamRenderEntity( void ) {
2617 const idDeclModelDef *modelDef;
2619 memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2620 steamRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
2621 steamRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
2622 steamRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
2624 temp = spawnArgs.GetString ( "model_steam" );
2625 if ( *temp != '\0' ) {
2626 if ( !strstr( temp, "." ) ) {
2627 modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, temp, false ) );
2629 steamRenderEntity.hModel = modelDef->ModelHandle();
2633 if ( !steamRenderEntity.hModel ) {
2634 steamRenderEntity.hModel = renderModelManager->FindModel( temp );
2637 if ( steamRenderEntity.hModel ) {
2638 steamRenderEntity.bounds = steamRenderEntity.hModel->Bounds( &steamRenderEntity );
2640 steamRenderEntity.bounds.Zero();
2642 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2643 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2644 steamModelDefHandle = gameRenderWorld->AddEntityDef( &steamRenderEntity );
2650 idAFEntity_SteamPipe::Think
2653 void idAFEntity_SteamPipe::Think( void ) {
2656 if ( thinkFlags & TH_THINK ) {
2657 steamDir.x = gameLocal.random.CRandomFloat() * steamForce;
2658 steamDir.y = gameLocal.random.CRandomFloat() * steamForce;
2659 steamDir.z = steamUpForce;
2660 force.SetForce( steamDir );
2661 force.Evaluate( gameLocal.time );
2662 //gameRenderWorld->DebugArrow( colorWhite, af.GetPhysics()->GetOrigin( steamBody ), af.GetPhysics()->GetOrigin( steamBody ) - 10.0f * steamDir, 4 );
2665 if ( steamModelDefHandle >= 0 ){
2666 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2667 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2668 gameRenderWorld->UpdateEntityDef( steamModelDefHandle, &steamRenderEntity );
2671 idAFEntity_Base::Think();
2676 ===============================================================================
2678 idAFEntity_ClawFourFingers
2680 ===============================================================================
2683 const idEventDef EV_SetFingerAngle( "setFingerAngle", "f" );
2684 const idEventDef EV_StopFingers( "stopFingers" );
2686 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_ClawFourFingers )
2687 EVENT( EV_SetFingerAngle, idAFEntity_ClawFourFingers::Event_SetFingerAngle )
2688 EVENT( EV_StopFingers, idAFEntity_ClawFourFingers::Event_StopFingers )
2691 static const char *clawConstraintNames[] = {
2692 "claw1", "claw2", "claw3", "claw4"
2697 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers
2700 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers( void ) {
2709 idAFEntity_ClawFourFingers::Save
2712 void idAFEntity_ClawFourFingers::Save( idSaveGame *savefile ) const {
2715 for ( i = 0; i < 4; i++ ) {
2716 fingers[i]->Save( savefile );
2722 idAFEntity_ClawFourFingers::Restore
2725 void idAFEntity_ClawFourFingers::Restore( idRestoreGame *savefile ) {
2728 for ( i = 0; i < 4; i++ ) {
2729 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2730 fingers[i]->Restore( savefile );
2739 idAFEntity_ClawFourFingers::Spawn
2742 void idAFEntity_ClawFourFingers::Spawn( void ) {
2749 af.GetPhysics()->LockWorldConstraints( true );
2750 af.GetPhysics()->SetForcePushable( true );
2751 SetPhysics( af.GetPhysics() );
2753 fl.takedamage = true;
2755 for ( i = 0; i < 4; i++ ) {
2756 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2757 if ( !fingers[i] ) {
2758 gameLocal.Error( "idClaw_FourFingers '%s': can't find claw constraint '%s'", name.c_str(), clawConstraintNames[i] );
2765 idAFEntity_ClawFourFingers::Event_SetFingerAngle
2768 void idAFEntity_ClawFourFingers::Event_SetFingerAngle( float angle ) {
2771 for ( i = 0; i < 4; i++ ) {
2772 fingers[i]->SetSteerAngle( angle );
2773 fingers[i]->SetSteerSpeed( 0.5f );
2775 af.GetPhysics()->Activate();
2780 idAFEntity_ClawFourFingers::Event_StopFingers
2783 void idAFEntity_ClawFourFingers::Event_StopFingers( void ) {
2786 for ( i = 0; i < 4; i++ ) {
2787 fingers[i]->SetSteerAngle( fingers[i]->GetAngle() );
2793 ===============================================================================
2795 editor support routines
2797 ===============================================================================
2803 idGameEdit::AF_SpawnEntity
2806 bool idGameEdit::AF_SpawnEntity( const char *fileName ) {
2809 idAFEntity_Generic *ent;
2814 player = gameLocal.GetLocalPlayer();
2815 if ( !player || !gameLocal.CheatsOk( false ) ) {
2819 af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, fileName ) );
2824 yaw = player->viewAngles.yaw;
2825 args.Set( "angle", va( "%f", yaw + 180 ) );
2826 org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
2827 args.Set( "origin", org.ToString() );
2828 args.Set( "spawnclass", "idAFEntity_Generic" );
2829 if ( af->model[0] ) {
2830 args.Set( "model", af->model.c_str() );
2832 args.Set( "model", fileName );
2834 if ( af->skin[0] ) {
2835 args.Set( "skin", af->skin.c_str() );
2837 args.Set( "articulatedFigure", fileName );
2838 args.Set( "nodrop", "1" );
2839 ent = static_cast<idAFEntity_Generic *>(gameLocal.SpawnEntityType( idAFEntity_Generic::Type, &args));
2841 // always update this entity
2842 ent->BecomeActive( TH_THINK );
2843 ent->KeepRunningPhysics();
2844 ent->fl.forcePhysicsUpdate = true;
2846 player->dragEntity.SetSelected( ent );
2853 idGameEdit::AF_UpdateEntities
2856 void idGameEdit::AF_UpdateEntities( const char *fileName ) {
2858 idAFEntity_Base *af;
2862 name.StripFileExtension();
2864 // reload any idAFEntity_Generic which uses the given articulated figure file
2865 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2866 if ( ent->IsType( idAFEntity_Base::Type ) ) {
2867 af = static_cast<idAFEntity_Base *>(ent);
2868 if ( name.Icmp( af->GetAFName() ) == 0 ) {
2870 af->GetAFPhysics()->PutToRest();
2878 idGameEdit::AF_UndoChanges
2881 void idGameEdit::AF_UndoChanges( void ) {
2884 idAFEntity_Base *af;
2887 c = declManager->GetNumDecls( DECL_AF );
2888 for ( i = 0; i < c; i++ ) {
2889 decl = static_cast<idDeclAF *>( const_cast<idDecl *>( declManager->DeclByIndex( DECL_AF, i, false ) ) );
2890 if ( !decl->modified ) {
2895 declManager->FindType( DECL_AF, decl->GetName() );
2897 // reload all AF entities using the file
2898 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2899 if ( ent->IsType( idAFEntity_Base::Type ) ) {
2900 af = static_cast<idAFEntity_Base *>(ent);
2901 if ( idStr::Icmp( decl->GetName(), af->GetAFName() ) == 0 ) {
2915 renderEntity_t *ent;
2916 const idMD5Joint *joints;
2917 } jointTransformData_t;
2919 static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
2921 jointTransformData_t *data = reinterpret_cast<jointTransformData_t *>(model);
2923 for ( i = 0; i < data->ent->numJoints; i++ ) {
2924 if ( data->joints[i].name.Icmp( jointName ) == 0 ) {
2928 if ( i >= data->ent->numJoints ) {
2931 origin = frame[i].ToVec3();
2932 axis = frame[i].ToMat3();
2941 static const char *GetArgString( const idDict &args, const idDict *defArgs, const char *key ) {
2944 s = args.GetString( key );
2945 if ( !s[0] && defArgs ) {
2946 s = defArgs->GetString( key );
2953 idGameEdit::AF_CreateMesh
2956 idRenderModel *idGameEdit::AF_CreateMesh( const idDict &args, idVec3 &meshOrigin, idMat3 &meshAxis, bool &poseIsSet ) {
2959 const idDeclAF_Body *fb;
2961 idVec3 origin, *bodyOrigin, *newBodyOrigin, *modifiedOrigin;
2962 idMat3 axis, *bodyAxis, *newBodyAxis, *modifiedAxis;
2963 declAFJointMod_t *jointMod;
2965 const idDict *defArgs;
2966 const idKeyValue *arg;
2968 jointTransformData_t data;
2969 const char *classname, *afName, *modelName;
2971 const idDeclModelDef *modelDef;
2972 const idMD5Anim *MD5anim;
2973 const idMD5Joint *MD5joint;
2974 const idMD5Joint *MD5joints;
2976 idJointMat *originalJoints;
2981 meshAxis.Identity();
2983 classname = args.GetString( "classname" );
2984 defArgs = gameLocal.FindEntityDefDict( classname );
2986 // get the articulated figure
2987 afName = GetArgString( args, defArgs, "articulatedFigure" );
2988 af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, afName ) );
2993 // get the md5 model
2994 modelName = GetArgString( args, defArgs, "model" );
2995 modelDef = static_cast< const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
3000 // make sure model hasn't been purged
3001 if ( modelDef->ModelHandle() && !modelDef->ModelHandle()->IsLoaded() ) {
3002 modelDef->ModelHandle()->LoadModel();
3006 md5 = modelDef->ModelHandle();
3007 if ( !md5 || md5->IsDefaultModel() ) {
3011 // get the articulated figure pose anim
3012 int animNum = modelDef->GetAnim( "af_pose" );
3016 const idAnim *anim = modelDef->GetAnim( animNum );
3020 MD5anim = anim->MD5Anim( 0 );
3021 MD5joints = md5->GetJoints();
3022 numMD5joints = md5->NumJoints();
3024 // setup a render entity
3025 memset( &ent, 0, sizeof( ent ) );
3026 ent.customSkin = modelDef->GetSkin();
3028 ent.numJoints = numMD5joints;
3029 ent.joints = ( idJointMat * )_alloca16( ent.numJoints * sizeof( *ent.joints ) );
3031 // create animation from of the af_pose
3032 ANIM_CreateAnimFrame( md5, MD5anim, ent.numJoints, ent.joints, 1, modelDef->GetVisualOffset(), false );
3034 // buffers to store the initial origin and axis for each body
3035 bodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
3036 bodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
3037 newBodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
3038 newBodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
3040 // finish the AF positions
3042 data.joints = MD5joints;
3043 af->Finish( GetJointTransform, ent.joints, &data );
3045 // get the initial origin and axis for each AF body
3046 for ( i = 0; i < af->bodies.Num(); i++ ) {
3049 if ( fb->modelType == TRM_BONE ) {
3050 // axis of bone trace model
3051 axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
3052 axis[2].Normalize();
3053 axis[2].NormalVectors( axis[0], axis[1] );
3056 axis = fb->angles.ToMat3();
3059 newBodyOrigin[i] = bodyOrigin[i] = fb->origin.ToVec3();
3060 newBodyAxis[i] = bodyAxis[i] = axis;
3063 // get any new body transforms stored in the key/value pairs
3064 for ( arg = args.MatchPrefix( "body ", NULL ); arg; arg = args.MatchPrefix( "body ", arg ) ) {
3065 name = arg->GetKey();
3066 name.Strip( "body " );
3067 for ( i = 0; i < af->bodies.Num(); i++ ) {
3069 if ( fb->name.Icmp( name ) == 0 ) {
3073 if ( i >= af->bodies.Num() ) {
3076 sscanf( arg->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
3078 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
3079 meshAxis = bodyAxis[i].Transpose() * angles.ToMat3();
3080 meshOrigin = origin - bodyOrigin[i] * meshAxis;
3083 newBodyOrigin[i] = origin;
3084 newBodyAxis[i] = angles.ToMat3();
3088 // save the original joints
3089 originalJoints = ( idJointMat * )_alloca16( numMD5joints * sizeof( originalJoints[0] ) );
3090 memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) );
3092 // buffer to store the joint mods
3093 jointMod = (declAFJointMod_t *) _alloca16( numMD5joints * sizeof( declAFJointMod_t ) );
3094 memset( jointMod, -1, numMD5joints * sizeof( declAFJointMod_t ) );
3095 modifiedOrigin = (idVec3 *) _alloca16( numMD5joints * sizeof( idVec3 ) );
3096 memset( modifiedOrigin, 0, numMD5joints * sizeof( idVec3 ) );
3097 modifiedAxis = (idMat3 *) _alloca16( numMD5joints * sizeof( idMat3 ) );
3098 memset( modifiedAxis, 0, numMD5joints * sizeof( idMat3 ) );
3100 // get all the joint modifications
3101 for ( i = 0; i < af->bodies.Num(); i++ ) {
3104 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
3108 for ( jointNum = 0; jointNum < numMD5joints; jointNum++ ) {
3109 if ( MD5joints[jointNum].name.Icmp( fb->jointName ) == 0 ) {
3114 if ( jointNum >= 0 && jointNum < ent.numJoints ) {
3115 jointMod[ jointNum ] = fb->jointMod;
3116 modifiedAxis[ jointNum ] = ( bodyAxis[i] * originalJoints[jointNum].ToMat3().Transpose() ).Transpose() * ( newBodyAxis[i] * meshAxis.Transpose() );
3117 // FIXME: calculate correct modifiedOrigin
3118 modifiedOrigin[ jointNum ] = originalJoints[ jointNum ].ToVec3();
3122 // apply joint modifications to the skeleton
3123 MD5joint = MD5joints + 1;
3124 for( i = 1; i < numMD5joints; i++, MD5joint++ ) {
3126 parentNum = MD5joint->parent - MD5joints;
3127 idMat3 parentAxis = originalJoints[ parentNum ].ToMat3();
3128 idMat3 localm = originalJoints[i].ToMat3() * parentAxis.Transpose();
3129 idVec3 localt = ( originalJoints[i].ToVec3() - originalJoints[ parentNum ].ToVec3() ) * parentAxis.Transpose();
3131 switch( jointMod[i] ) {
3132 case DECLAF_JOINTMOD_ORIGIN: {
3133 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
3134 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
3137 case DECLAF_JOINTMOD_AXIS: {
3138 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
3139 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
3142 case DECLAF_JOINTMOD_BOTH: {
3143 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
3144 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
3148 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
3149 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
3155 // instantiate a mesh using the joint information from the render entity
3156 return md5->InstantiateDynamicModel( &ent, NULL, NULL );
3162 ===============================================================================
3164 ===============================================================================
3167 const idEventDef EV_Harvest_SpawnHarvestTrigger( "<spawnHarvestTrigger>", NULL );
3169 CLASS_DECLARATION( idEntity, idHarvestable )
3170 EVENT( EV_Harvest_SpawnHarvestTrigger, idHarvestable::Event_SpawnHarvestTrigger )
3171 EVENT( EV_Touch, idHarvestable::Event_Touch )
3174 idHarvestable::idHarvestable() {
3179 idHarvestable::~idHarvestable() {
3186 void idHarvestable::Spawn() {
3190 spawnArgs.GetFloat( "triggersize", "120", triggersize );
3191 spawnArgs.GetFloat( "give_delay", "3", giveDelay);
3195 removeDelay = spawnArgs.GetFloat( "remove_delay") * 1000.0f;
3197 fxFollowPlayer = spawnArgs.GetBool("fx_follow_player", "1");
3198 fxOrient = spawnArgs.GetString("fx_orient");
3203 void idHarvestable::Init(idEntity* parent) {
3209 GetPhysics()->SetOrigin( parent->GetPhysics()->GetOrigin() );
3210 this->Bind(parent, true);
3212 //Set the skin of the entity to the harvest skin
3213 idStr skin = parent->spawnArgs.GetString("skin_harvest", "");
3215 parent->SetSkin(declManager->FindSkin(skin.c_str()));
3219 if(parent->IsType(idActor::Type)) {
3220 idActor* withHead = (idActor*)parent;
3221 head = withHead->GetHeadEntity();
3223 if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
3224 idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
3225 head = withHead->head.GetEntity();
3228 idStr headskin = parent->spawnArgs.GetString("skin_harvest_head", "");
3229 if(headskin.Length()) {
3230 head->SetSkin(declManager->FindSkin(headskin.c_str()));
3234 idStr sound = parent->spawnArgs.GetString("harvest_sound");
3235 if(sound.Length() > 0) {
3236 parent->StartSound( sound.c_str(), SND_CHANNEL_ANY, 0, false, NULL);
3240 PostEventMS( &EV_Harvest_SpawnHarvestTrigger, 0 );
3243 void idHarvestable::Save( idSaveGame *savefile ) const {
3244 savefile->WriteFloat( triggersize );
3245 savefile->WriteClipModel( trigger );
3246 savefile->WriteFloat( giveDelay );
3247 savefile->WriteFloat( removeDelay );
3248 savefile->WriteBool( given );
3250 player.Save( savefile );
3251 savefile->WriteInt( startTime );
3253 savefile->WriteBool( fxFollowPlayer );
3254 fx.Save( savefile );
3255 savefile->WriteString( fxOrient );
3257 parentEnt.Save(savefile);
3260 void idHarvestable::Restore( idRestoreGame *savefile ) {
3261 savefile->ReadFloat( triggersize );
3262 savefile->ReadClipModel( trigger );
3263 savefile->ReadFloat( giveDelay );
3264 savefile->ReadFloat( removeDelay );
3265 savefile->ReadBool( given );
3267 player.Restore( savefile );
3268 savefile->ReadInt( startTime );
3270 savefile->ReadBool( fxFollowPlayer );
3271 fx.Restore( savefile );
3272 savefile->ReadString( fxOrient );
3274 parentEnt.Restore(savefile);
3277 void idHarvestable::SetParent(idEntity* parent) {
3281 void idHarvestable::Think() {
3283 idEntity* parent = parentEnt.GetEntity();
3288 //Update the orientation of the box
3289 if(trigger && parent && !parent->GetPhysics()->IsAtRest()) {
3290 trigger->Link( gameLocal.clip, this, 0, parent->GetPhysics()->GetOrigin(), parent->GetPhysics()->GetAxis());
3293 if(startTime && gameLocal.slow.time - startTime > giveDelay && ! given) {
3294 idPlayer *thePlayer = player.GetEntity();
3296 thePlayer->Give(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"));
3297 thePlayer->harvest_lock = false;
3301 if(startTime && gameLocal.slow.time - startTime > removeDelay) {
3302 parent->PostEventMS( &EV_Remove, 0 );
3303 PostEventMS( &EV_Remove, 0 );
3306 if(fxFollowPlayer) {
3307 idEntityFx* fxEnt = fx.GetEntity();
3310 idMat3 orientAxisLocal;
3311 if(GetFxOrientationAxis(orientAxisLocal)) {
3312 //gameRenderWorld->DebugAxis(fxEnt->GetPhysics()->GetOrigin(), orientAxisLocal);
3313 fxEnt->GetPhysics()->SetAxis(orientAxisLocal);
3321 idAFEntity_Harvest::Gib
3322 Called when the parent object has been gibbed.
3325 void idHarvestable::Gib() {
3326 //Stop any looping sound that was playing
3327 idEntity* parent = parentEnt.GetEntity();
3329 idStr sound = parent->spawnArgs.GetString("harvest_sound");
3330 if(sound.Length() > 0) {
3331 parent->StopSound(SND_CHANNEL_ANY, false);
3338 idAFEntity_Harvest::BeginBurn
3341 void idHarvestable::BeginBurn() {
3343 idEntity* parent = parentEnt.GetEntity();
3348 if(!spawnArgs.GetBool("burn")) {
3353 //Switch Skins if the parent would like us to.
3354 idStr skin = parent->spawnArgs.GetString("skin_harvest_burn", "");
3356 parent->SetSkin(declManager->FindSkin(skin.c_str()));
3358 parent->GetRenderEntity()->noShadow = true;
3359 parent->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
3362 if(parent->IsType(idActor::Type)) {
3363 idActor* withHead = (idActor*)parent;
3364 head = withHead->GetHeadEntity();
3366 if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
3367 idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
3368 head = withHead->head.GetEntity();
3371 idStr headskin = parent->spawnArgs.GetString("skin_harvest_burn_head", "");
3372 if(headskin.Length()) {
3373 head->SetSkin(declManager->FindSkin(headskin.c_str()));
3376 head->GetRenderEntity()->noShadow = true;
3377 head->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
3386 idAFEntity_Harvest::BeginFX
3389 void idHarvestable::BeginFX() {
3390 if(strlen(spawnArgs.GetString("fx")) <= 0) {
3394 idMat3* orientAxis = NULL;
3395 idMat3 orientAxisLocal;
3397 if(GetFxOrientationAxis(orientAxisLocal)) {
3398 orientAxis = &orientAxisLocal;
3400 fx = idEntityFx::StartFx( spawnArgs.GetString("fx"), NULL, orientAxis, this, spawnArgs.GetBool("fx_bind") );
3405 idAFEntity_Harvest::CalcTriggerBounds
3408 void idHarvestable::CalcTriggerBounds( float size, idBounds &bounds ) {
3410 idEntity* parent = parentEnt.GetEntity();
3415 //Simple trigger bounds is the absolute bounds of the AF plus a defined size
3416 bounds = parent->GetPhysics()->GetAbsBounds();
3417 bounds.ExpandSelf(size);
3418 bounds[0] -= parent->GetPhysics()->GetOrigin();
3419 bounds[1] -= parent->GetPhysics()->GetOrigin();
3422 bool idHarvestable::GetFxOrientationAxis(idMat3& mat) {
3424 idEntity* parent = parentEnt.GetEntity();
3429 idPlayer *thePlayer = player.GetEntity();
3431 if(!fxOrient.Icmp("up")) {
3433 idVec3 grav = parent->GetPhysics()->GetGravityNormal()*-1;
3436 grav.OrthogonalBasis(left, up);
3437 idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, grav.x, grav.y, grav.z);
3442 } else if(!fxOrient.Icmp("weapon")) {
3443 //Orient the fx towards the muzzle of the weapon
3444 jointHandle_t joint;
3445 idVec3 joint_origin;
3448 joint = thePlayer->weapon.GetEntity()->GetAnimator()->GetJointHandle( spawnArgs.GetString("fx_weapon_joint") );
3449 if ( joint != INVALID_JOINT ) {
3450 thePlayer->weapon.GetEntity()->GetJointWorldTransform( joint, gameLocal.slow.time, joint_origin, joint_axis );
3452 joint_origin = thePlayer->GetPhysics()->GetOrigin();
3455 idVec3 toPlayer = joint_origin-parent->GetPhysics()->GetOrigin();
3456 toPlayer.NormalizeFast();
3459 toPlayer.OrthogonalBasis(left, up);
3460 idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
3465 } else if(!fxOrient.Icmp("player")) {
3466 //Orient the fx towards the eye of the player
3467 idVec3 eye = thePlayer->GetEyePosition();
3468 idVec3 toPlayer = eye-parent->GetPhysics()->GetOrigin();
3470 toPlayer.Normalize();
3474 left = toPlayer.Cross(up);
3475 up = left.Cross(toPlayer);
3478 //common->Printf("%.2f %.2f %.2f - %.2f %.2f %.2f - %.2f %.2f %.2f\n", toPlayer.x, toPlayer.y, toPlayer.z, left.x, left.y, left.z, up.x, up.y, up.z );
3480 idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
3487 //Returning false indicates that the orientation is not used;
3493 idAFEntity_Harvest::Event_SpawnHarvestTrigger
3496 void idHarvestable::Event_SpawnHarvestTrigger( void ) {
3499 idEntity* parent = parentEnt.GetEntity();
3504 CalcTriggerBounds( triggersize, bounds );
3506 // create a trigger clip model
3507 trigger = new idClipModel( idTraceModel( bounds ) );
3508 trigger->Link( gameLocal.clip, this, 255, parent->GetPhysics()->GetOrigin(), mat3_identity);
3509 trigger->SetContents( CONTENTS_TRIGGER );
3516 idAFEntity_Harvest::Event_Touch
3519 void idHarvestable::Event_Touch( idEntity *other, trace_t *trace ) {
3521 idEntity* parent = parentEnt.GetEntity();
3525 if(parent->IsType(idAFEntity_Gibbable::Type)) {
3526 idAFEntity_Gibbable* gibParent = (idAFEntity_Gibbable*)parent;
3527 if(gibParent->IsGibbed())
3532 if(!startTime && other && other->IsType(idPlayer::Type)) {
3533 idPlayer *thePlayer = static_cast<idPlayer *>(other);
3535 if(thePlayer->harvest_lock) {
3536 //Don't harvest if the player is in mid harvest
3542 bool okToGive = true;
3543 idStr requiredWeapons = spawnArgs.GetString("required_weapons");
3545 if(requiredWeapons.Length() > 0) {
3546 idStr playerWeap = thePlayer->GetCurrentWeapon();
3547 if(playerWeap.Length() == 0 || requiredWeapons.Find(playerWeap, false) == -1) {
3553 if(thePlayer->CanGive(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"))) {
3555 startTime = gameLocal.slow.time;
3557 //Lock the player from harvesting to prevent multiple harvests when only one is needed
3558 thePlayer->harvest_lock = true;
3560 idWeapon* weap = (idWeapon*)thePlayer->weapon.GetEntity();
3562 //weap->PostEventMS(&EV_Weapon_State, 0, "Charge", 8);
3563 weap->ProcessEvent(&EV_Weapon_State, "Charge", 8);
3569 //Stop any looping sound that was playing
3570 idStr sound = parent->spawnArgs.GetString("harvest_sound");
3571 if(sound.Length() > 0) {
3572 parent->StopSound(SND_CHANNEL_ANY, false);
3575 //Make the parent object non-solid
3576 parent->GetPhysics()->SetContents( 0 );
3577 parent->GetPhysics()->GetClipModel()->Unlink();
3579 //Turn of the trigger so it doesn't process twice
3580 trigger->SetContents( 0 );
3588 ===============================================================================
3592 ===============================================================================
3595 const idEventDef EV_Harvest_SpawnHarvestEntity( "<spawnHarvestEntity>", NULL );
3597 CLASS_DECLARATION( idAFEntity_WithAttachedHead, idAFEntity_Harvest )
3598 EVENT( EV_Harvest_SpawnHarvestEntity, idAFEntity_Harvest::Event_SpawnHarvestEntity )
3603 idAFEntity_Harvest::idAFEntity_Harvest
3606 idAFEntity_Harvest::idAFEntity_Harvest() {
3612 idAFEntity_Harvest::~idAFEntity_Harvest
3615 idAFEntity_Harvest::~idAFEntity_Harvest() {
3617 if ( harvestEnt.GetEntity() ) {
3618 harvestEnt.GetEntity()->PostEventMS( &EV_Remove, 0 );
3625 idAFEntity_Harvest::Save
3628 void idAFEntity_Harvest::Save( idSaveGame *savefile ) const {
3629 harvestEnt.Save(savefile);
3634 idAFEntity_Harvest::Restore
3637 void idAFEntity_Harvest::Restore( idRestoreGame *savefile ) {
3638 harvestEnt.Restore(savefile);
3639 //if(harvestEnt.GetEntity()) {
3640 // harvestEnt.GetEntity()->SetParent(this);
3646 idAFEntity_Harvest::Spawn
3649 void idAFEntity_Harvest::Spawn( void ) {
3651 PostEventMS( &EV_Harvest_SpawnHarvestEntity, 0 );
3656 idAFEntity_Harvest::Think
3659 void idAFEntity_Harvest::Think( void ) {
3661 idAFEntity_WithAttachedHead::Think();
3665 void idAFEntity_Harvest::Event_SpawnHarvestEntity( void ) {
3667 const idDict *harvestDef = gameLocal.FindEntityDefDict( spawnArgs.GetString("def_harvest_type"), false );
3670 gameLocal.SpawnEntityDef( *harvestDef, &temp, false );
3671 harvestEnt = static_cast<idHarvestable *>(temp);
3674 if(harvestEnt.GetEntity()) {
3675 //Let the harvest entity set itself up
3676 harvestEnt.GetEntity()->Init(this);
3677 harvestEnt.GetEntity()->BecomeActive( TH_THINK );
3681 void idAFEntity_Harvest::Gib( const idVec3 &dir, const char *damageDefName ) {
3682 if(harvestEnt.GetEntity()) {
3683 //Let the harvest ent know that we gibbed
3684 harvestEnt.GetEntity()->Gib();
3686 idAFEntity_WithAttachedHead::Gib(dir, damageDefName);