#include "../idlib/precompiled.h" #pragma hdrstop #include "Game_local.h" #include "Projectile.h" /* =============================================================================== idMultiModelAF =============================================================================== */ CLASS_DECLARATION( idEntity, idMultiModelAF ) END_CLASS /* ================ idMultiModelAF::Spawn ================ */ void idMultiModelAF::Spawn( void ) { physicsObj.SetSelf( this ); } /* ================ idMultiModelAF::~idMultiModelAF ================ */ idMultiModelAF::~idMultiModelAF( void ) { int i; for ( i = 0; i < modelDefHandles.Num(); i++ ) { if ( modelDefHandles[i] != -1 ) { gameRenderWorld->FreeEntityDef( modelDefHandles[i] ); modelDefHandles[i] = -1; } } SetPhysics( NULL ); } /* ================ idMultiModelAF::SetModelForId ================ */ void idMultiModelAF::SetModelForId( int id, const idStr &modelName ) { modelHandles.AssureSize( id+1, NULL ); modelDefHandles.AssureSize( id+1, -1 ); modelHandles[id] = renderModelManager->FindModel( modelName ); } /* ================ idMultiModelAF::Present ================ */ void idMultiModelAF::Present( void ) { int i; // don't present to the renderer if the entity hasn't changed if ( !( thinkFlags & TH_UPDATEVISUALS ) ) { return; } BecomeInactive( TH_UPDATEVISUALS ); for ( i = 0; i < modelHandles.Num(); i++ ) { if ( !modelHandles[i] ) { continue; } renderEntity.origin = physicsObj.GetOrigin( i ); renderEntity.axis = physicsObj.GetAxis( i ); renderEntity.hModel = modelHandles[i]; renderEntity.bodyId = i; // add to refresh list if ( modelDefHandles[i] == -1 ) { modelDefHandles[i] = gameRenderWorld->AddEntityDef( &renderEntity ); } else { gameRenderWorld->UpdateEntityDef( modelDefHandles[i], &renderEntity ); } } } /* ================ idMultiModelAF::Think ================ */ void idMultiModelAF::Think( void ) { RunPhysics(); Present(); } /* =============================================================================== idChain =============================================================================== */ CLASS_DECLARATION( idMultiModelAF, idChain ) END_CLASS /* ================ idChain::BuildChain builds a chain hanging down from the ceiling the highest link is a child of the link below it etc. this allows an object to be attached to multiple chains while keeping a single tree structure ================ */ void idChain::BuildChain( const idStr &name, const idVec3 &origin, float linkLength, float linkWidth, float density, int numLinks, bool bindToWorld ) { int i; float halfLinkLength = linkLength * 0.5f; idTraceModel trm; idClipModel *clip; idAFBody *body, *lastBody; idAFConstraint_BallAndSocketJoint *bsj; idAFConstraint_UniversalJoint *uj; idVec3 org; // create a trace model trm = idTraceModel( linkLength, linkWidth ); trm.Translate( -trm.offset ); org = origin - idVec3( 0, 0, halfLinkLength ); lastBody = NULL; for ( i = 0; i < numLinks; i++ ) { // add body // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END clip = new idClipModel( trm ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END clip->SetContents( CONTENTS_SOLID ); // RAVEN BEGIN // ddynerman: multiple clip worlds clip->Link( this, 0, org, mat3_identity ); // RAVEN END // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END body = new idAFBody( name + idStr(i), clip, density ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END physicsObj.AddBody( body ); // visual model for body SetModelForId( physicsObj.GetBodyId( body ), spawnArgs.GetString( "model" ) ); // add constraint if ( bindToWorld ) { if ( !lastBody ) { // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END uj = new idAFConstraint_UniversalJoint( name + idStr(i), body, lastBody ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END uj->SetShafts( idVec3( 0, 0, -1 ), idVec3( 0, 0, 1 ) ); //uj->SetConeLimit( idVec3( 0, 0, -1 ), 30.0f ); //uj->SetPyramidLimit( idVec3( 0, 0, -1 ), idVec3( 1, 0, 0 ), 90.0f, 30.0f ); } else { // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END uj = new idAFConstraint_UniversalJoint( name + idStr(i), lastBody, body ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END uj->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) ); //uj->SetConeLimit( idVec3( 0, 0, 1 ), 30.0f ); } uj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) ); uj->SetFriction( 0.9f ); physicsObj.AddConstraint( uj ); } else { if ( lastBody ) { // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END bsj = new idAFConstraint_BallAndSocketJoint( "joint" + idStr(i), lastBody, body ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END bsj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) ); bsj->SetConeLimit( idVec3( 0, 0, 1 ), 60.0f, idVec3( 0, 0, 1 ) ); physicsObj.AddConstraint( bsj ); } } org[2] -= linkLength; lastBody = body; } } /* ================ idChain::Spawn ================ */ void idChain::Spawn( void ) { int numLinks; float length, linkLength, linkWidth, density; bool drop; idVec3 origin; spawnArgs.GetBool( "drop", "0", drop ); spawnArgs.GetInt( "links", "3", numLinks ); spawnArgs.GetFloat( "length", idStr( numLinks * 32.0f ), length ); spawnArgs.GetFloat( "width", "8", linkWidth ); spawnArgs.GetFloat( "density", "0.2", density ); linkLength = length / numLinks; origin = GetPhysics()->GetOrigin(); // initialize physics physicsObj.SetSelf( this ); physicsObj.SetGravity( gameLocal.GetGravity() ); physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY ); SetPhysics( &physicsObj ); BuildChain( "link", origin, linkLength, linkWidth, density, numLinks, !drop ); } /* =============================================================================== idAFAttachment =============================================================================== */ const idEventDef EV_ClearAnims( "clearAnims" ); CLASS_DECLARATION( idAnimatedEntity, idAFAttachment ) // RAVEN BEGIN // jshepard: we'd like these to animate. EVENT( AI_PlayAnim, idAFAttachment::Event_PlayAnim ) EVENT( AI_PlayCycle, idAFAttachment::Event_PlayCycle ) EVENT( EV_ClearAnims, idAFAttachment::Event_ClearAnims ) // RAVEN END END_CLASS /* ===================== idAFAttachment::idAFAttachment ===================== */ idAFAttachment::idAFAttachment( void ) { body = NULL; combatModel = NULL; idleAnim = 0; damageJoint = INVALID_JOINT; // RAVEN BEGIN // jscott: Lip Syncing variables lipSyncAnim = 0; lipSyncData = NULL; soundJoint = INVALID_JOINT; // MCG noPlayerImpactFX = false; // RAVEN END } /* ===================== idAFAttachment::~idAFAttachment ===================== */ idAFAttachment::~idAFAttachment( void ) { StopSound( SND_CHANNEL_ANY, false ); delete combatModel; combatModel = NULL; // RAVEN BEGIN EndLipSyncing(); // RAVEN END } /* ===================== idAFAttachment::Spawn ===================== */ void idAFAttachment::Spawn( void ) { idStr jointName; idleAnim = animator.GetAnim( "idle" ); // RAVEN BEGIN // Init the lip sync data lipSyncAnim = 0; if( spawnArgs.GetInt( "uses_lip_syncing", "0" ) ) { lipSyncAnim = animator.GetAnim( "lipsync" ); } // Grab the joint to play sounds off soundJoint = INVALID_JOINT; spawnArgs.GetString( "sound_bone", "", jointName ); // Do we want a bone to play sounds on? if( jointName.Length() ) { // Try to find the specified bone soundJoint = animator.GetJointHandle( jointName ); if ( soundJoint == INVALID_JOINT ) { // The def specified a bone for this and we can't find it - warn them. gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), jointName.c_str() ); } } noPlayerImpactFX = spawnArgs.GetBool( "noPlayerImpactFX", "0" ); // RAVEN END } /* ===================== idAFAttachment::InitCopyJoints ===================== */ void idAFAttachment::InitCopyJoints ( void ) { copyJoints_t copyJoint; const idKeyValue* kv; const char* jointName; idAnimator* bodyAnimator; if ( !body ) { return; } bodyAnimator = body->GetAnimator ( ); // set up the list of joints to copy to the head for( kv = spawnArgs.MatchPrefix( "copy_joint", NULL ); kv != NULL; kv = spawnArgs.MatchPrefix( "copy_joint", kv ) ) { if ( kv->GetValue() == "" ) { // probably clearing out inherited key, so skip it continue; } if ( !body->spawnArgs.GetString ( va("copy_joint_world %s", kv->GetValue().c_str() ), kv->GetValue().c_str(), &jointName ) ) { copyJoint.mod = JOINTMOD_LOCAL_OVERRIDE; body->spawnArgs.GetString ( va("copy_joint %s", kv->GetValue().c_str() ), kv->GetValue().c_str(), &jointName ); } else { copyJoint.mod = JOINTMOD_WORLD_OVERRIDE; } copyJoint.from = bodyAnimator->GetJointHandle ( jointName ); if ( copyJoint.from == INVALID_JOINT ) { gameLocal.Warning( "Unknown copy_joint '%s' on entity %s", jointName, name.c_str() ); continue; } copyJoint.to = animator.GetJointHandle( kv->GetValue() ); if ( copyJoint.to == INVALID_JOINT ) { gameLocal.Warning( "Unknown copy_joint '%s' on head of entity %s", kv->GetValue().c_str(), name.c_str() ); continue; } copyJoints.Append( copyJoint ); } } /* ===================== idAFAttachment::CopyJointsFromBody ===================== */ void idAFAttachment::CopyJointsFromBody ( void ) { MEM_SCOPED_TAG(tag,MA_ANIM); idAnimator* bodyAnimator; int i; idMat3 mat; idMat3 axis; idVec3 pos; if ( !body ) { return; } bodyAnimator = body->GetAnimator(); // copy the animation from the body to the head for( i = 0; i < copyJoints.Num(); i++ ) { if ( copyJoints[ i ].mod == JOINTMOD_WORLD_OVERRIDE ) { mat = GetPhysics()->GetAxis().Transpose(); body->GetJointWorldTransform( copyJoints[ i ].from, gameLocal.time, pos, axis ); pos -= GetPhysics()->GetOrigin(); animator.SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos * mat ); animator.SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis * mat ); } else { bodyAnimator->GetJointLocalTransform( copyJoints[ i ].from, gameLocal.time, pos, axis ); animator.SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos ); animator.SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis ); } } } /* ===================== idAFAttachment::SetBody ===================== */ void idAFAttachment::SetBody( idAnimatedEntity *bodyEnt, const char *model, jointHandle_t _damageJoint ) { body = bodyEnt; damageJoint = _damageJoint; SetModel( model ); fl.takedamage = true; spawnArgs.SetBool( "bleed", body->spawnArgs.GetBool( "bleed" ) ); } /* ===================== idAFAttachment::ClearBody ===================== */ void idAFAttachment::ClearBody( void ) { body = NULL; damageJoint = INVALID_JOINT; Hide(); } /* ===================== idAFAttachment::GetBody ===================== */ idEntity *idAFAttachment::GetBody( void ) const { return body; } /* ================ idAFAttachment::Save archive object for savegame file ================ */ void idAFAttachment::Save( idSaveGame *savefile ) const { int i; body.Save ( savefile ); savefile->WriteInt( idleAnim ); savefile->WriteJoint( damageJoint ); savefile->WriteJoint( soundJoint ); // RAVEN BEGIN // jscott: for lipsyncing savefile->WriteInt( lipSyncAnim ); // RAVEN END savefile->WriteInt( copyJoints.Num() ); for( i = 0; i < copyJoints.Num(); i++ ) { savefile->WriteInt( copyJoints[i].mod ); savefile->WriteJoint( copyJoints[i].from ); savefile->WriteJoint( copyJoints[i].to ); } bool hadCombatModel = false; if ( combatModel ) { hadCombatModel = true; } savefile->WriteBool( hadCombatModel ); savefile->WriteBool( noPlayerImpactFX ); } /* ================ idAFAttachment::Restore unarchives object from save game file ================ */ void idAFAttachment::Restore( idRestoreGame *savefile ) { body.Restore ( savefile ); savefile->ReadInt( idleAnim ); savefile->ReadJoint( damageJoint ); savefile->ReadJoint( soundJoint ); // RAVEN BEGIN // jscott: for lipsyncing savefile->ReadInt( lipSyncAnim ); // jscott: difficult to start a sound mid sentence and impossible to sync up the timing with the animation lipSyncData = NULL; // RAVEN END int i; int num; savefile->ReadInt( num ); copyJoints.SetNum( num ); for( i = 0; i < num; i++ ) { int val; savefile->ReadInt( val ); copyJoints[i].mod = static_cast( val ); savefile->ReadJoint( copyJoints[i].from ); savefile->ReadJoint( copyJoints[i].to ); } bool hadCombatModel; savefile->ReadBool( hadCombatModel ); if ( hadCombatModel ) { SetCombatModel(); LinkCombat(); } savefile->ReadBool( noPlayerImpactFX ); lipSyncAnim = 0; if( spawnArgs.GetInt( "uses_lip_syncing", "0" ) ) { lipSyncAnim = animator.GetAnim( "lipsync" ); } } /* ================ idAFAttachment::Hide ================ */ void idAFAttachment::Hide( void ) { idEntity::Hide(); UnlinkCombat(); } /* ================ idAFAttachment::Show ================ */ void idAFAttachment::Show( void ) { idEntity::Show(); LinkCombat(); } /* ============ idAFAttachment::Damage Pass damage to body at the bindjoint ============ */ void idAFAttachment::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) { if ( body ) { body->Damage( inflictor, attacker, dir, damageDefName, damageScale, damageJoint ); } } /* ================ idAFAttachment::AddDamageEffect ================ */ void idAFAttachment::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ) { if ( body ) { trace_t c = collision; c.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint ); body->AddDamageEffect( c, velocity, damageDefName, inflictor ); } } /* ================ idAFAttachment::GetImpactInfo ================ */ void idAFAttachment::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) { if ( body ) { body->GetImpactInfo( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint ), point, info ); } else { idEntity::GetImpactInfo( ent, id, point, info ); } } /* ================ idAFAttachment::CanPlayImpactEffect ================ */ bool idAFAttachment::CanPlayImpactEffect ( idEntity* attacker, idEntity* target ) { if ( ( noPlayerImpactFX && GetNoPlayerImpactFX( ) ) && attacker && attacker->IsType( idPlayer::GetClassType() ) ) { return false; } return idAnimatedEntity::CanPlayImpactEffect( attacker, target ); } /* ================ idAFAttachment::GetNoPlayerImpactFX ================ */ bool idAFAttachment::GetNoPlayerImpactFX( void ) { if ( GetTeamMaster( ) && this != GetTeamMaster( ) && GetTeamMaster( )->IsType( idAFEntity_Base::GetClassType( ) ) ) { return static_cast( GetTeamMaster( ) )->GetNoPlayerImpactFX( ); } else { return noPlayerImpactFX; } } /* ================ idAFAttachment::ApplyImpulse ================ */ void idAFAttachment::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) { if ( body ) { body->ApplyImpulse( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint ), point, impulse ); } else { idEntity::ApplyImpulse( ent, id, point, impulse ); } } /* ================ idAFAttachment::AddForce ================ */ void idAFAttachment::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) { if ( body ) { body->AddForce( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint ), point, force ); } else { idEntity::AddForce( ent, id, point, force ); } } /* ================ idAFAttachment::PlayIdleAnim ================ */ // RAVEN BEGIN // bdube: added channel void idAFAttachment::PlayIdleAnim( int channel, int blendTime ) { if ( idleAnim && ( idleAnim != animator.CurrentAnim( channel )->AnimNum() ) ) { animator.CycleAnim( channel, idleAnim, gameLocal.time, blendTime ); } // RAVEN END } /* ================ idAfAttachment::Think ================ */ void idAFAttachment::Think( void ) { // RAVEN BEGIN // jscott: Lip sync main code HandleLipSync(); // RAVEN END idAnimatedEntity::Think(); } // RAVEN BEGIN /* =============== idAnimated::GetPhysicsToSoundTransform =============== */ bool idAFAttachment::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) { if ( soundJoint != INVALID_JOINT ) { animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis ); axis = GetPhysics()->GetAxis(); } else { origin = GetPhysics()->GetOrigin(); axis = GetPhysics()->GetAxis(); } return true; } // RAVEN END /* ================ idAFAttachment::UpdateAnimationControllers ================ */ bool idAFAttachment::UpdateAnimationControllers( void ) { CopyJointsFromBody( ); return idAnimatedEntity::UpdateAnimationControllers( ); } /* ================ idAFAttachment::SetCombatModel ================ */ void idAFAttachment::SetCombatModel( void ) { if ( combatModel ) { combatModel->Unlink(); combatModel->LoadModel( modelDefHandle ); } else { // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END combatModel = new idClipModel( modelDefHandle ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END } combatModel->SetOwner( body ); } /* ================ idAFAttachment::GetCombatModel ================ */ idClipModel *idAFAttachment::GetCombatModel( void ) const { return combatModel; } /* ================ idAFAttachment::LinkCombat ================ */ void idAFAttachment::LinkCombat( void ) { if ( fl.hidden ) { return; } if ( combatModel ) { // RAVEN BEGIN // ddynerman: multiple clip worlds combatModel->Link( this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle ); // RAVEN END } } /* ================ idAFAttachment::UnlinkCombat ================ */ void idAFAttachment::UnlinkCombat( void ) { if ( combatModel ) { combatModel->Unlink(); } } // RAVEN BEGIN // bdube: return the body entity for damage /* ================ idAFAttachment::GetDamageEntity ================ */ idEntity* idAFAttachment::GetDamageEntity ( void ) { return body ? body : this; } // jshepard: we need to animate these /* ================ idAFAttachment::Event_PlayAnim ================ */ void idAFAttachment::Event_PlayAnim ( int channel, const char *animname ) { int anim; float animTime; float blendFrames = 4; anim = animator.GetAnim( animname ); if ( !anim ) { gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() ); animator.Clear( channel, gameLocal.time, FRAME2MS( blendFrames ) ); idThread::ReturnFloat( false ); } else { animator.PlayAnim( channel, anim, gameLocal.time, FRAME2MS( blendFrames ) ); animTime = animator.CurrentAnim( channel )->GetEndTime(); idThread::ReturnFloat( MS2SEC( animTime - gameLocal.time ) ); } blendFrames = 0; } // jdischler: and we want cycling, too. /* =============== idAFAttachment::Event_PlayCycle =============== */ void idAFAttachment::Event_PlayCycle( int channel, const char *animname ) { int anim; float animTime; float blendFrames = 4; anim = animator.GetAnim( animname ); if ( !anim ) { gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() ); animator.Clear( channel, gameLocal.time, FRAME2MS( blendFrames ) ); } else { animator.CycleAnim( channel, anim, gameLocal.time, FRAME2MS( blendFrames ) ); animTime = animator.CurrentAnim( channel )->GetEndTime(); } blendFrames = 0; } /* ================ idAFAttachment::Event_ClearAnims Clears any animation running on the idAFAttachment ================ */ void idAFAttachment::Event_ClearAnims( void ) { //animator.ClearAllAnims( gameLocal.time, 100 ); animator.Clear( ANIMCHANNEL_ALL, gameLocal.time, 0 ); } // RAVEN END /* =============================================================================== idAFEntity_Base =============================================================================== */ const idEventDef EV_SetConstraintPosition( "SetConstraintPosition", "sv" ); // RAVEN BEGIN // kfuller: added const idEventDef EV_TPose( "tpose", NULL ); // RAVEN END CLASS_DECLARATION( idAnimatedEntity, idAFEntity_Base ) EVENT( EV_SetConstraintPosition, idAFEntity_Base::Event_SetConstraintPosition ) END_CLASS static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f; static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f; /* ================ idAFEntity_Base::idAFEntity_Base ================ */ idAFEntity_Base::idAFEntity_Base( void ) { combatModel = NULL; combatModelContents = 0; nextSoundTime = 0; spawnOrigin.Zero(); spawnAxis.Identity(); noPlayerImpactFX = false; } /* ================ idAFEntity_Base::~idAFEntity_Base ================ */ idAFEntity_Base::~idAFEntity_Base( void ) { delete combatModel; combatModel = NULL; } /* ================ idAFEntity_Base::Save ================ */ void idAFEntity_Base::Save( idSaveGame *savefile ) const { savefile->WriteBool( noPlayerImpactFX ); savefile->WriteInt( combatModelContents ); savefile->WriteClipModel( combatModel ); savefile->WriteVec3( spawnOrigin ); savefile->WriteMat3( spawnAxis ); savefile->WriteInt( nextSoundTime ); af.Save( savefile ); } /* ================ idAFEntity_Base::Restore ================ */ void idAFEntity_Base::Restore( idRestoreGame *savefile ) { savefile->ReadBool( noPlayerImpactFX ); savefile->ReadInt( combatModelContents ); savefile->ReadClipModel( combatModel ); savefile->ReadVec3( spawnOrigin ); savefile->ReadMat3( spawnAxis ); savefile->ReadInt( nextSoundTime ); LinkCombat(); af.Restore( savefile ); } /* ================ idAFEntity_Base::Spawn ================ */ void idAFEntity_Base::Spawn( void ) { spawnOrigin = GetPhysics()->GetOrigin(); spawnAxis = GetPhysics()->GetAxis(); nextSoundTime = 0; noPlayerImpactFX = spawnArgs.GetBool( "noPlayerImpactFX", "0" ); } /* ================ idAFEntity_Base::LoadAF ================ */ bool idAFEntity_Base::LoadAF( const char* keyname ) { idStr fileName; if ( !keyname || !*keyname ) { keyname = "articulatedFigure"; } if ( !spawnArgs.GetString( keyname, "*unknown*", fileName ) ) { return false; } af.SetAnimator( GetAnimator() ); if ( !af.Load( this, fileName ) ) { gameLocal.Error( "idAFEntity_Base::LoadAF: Couldn't load af file '%s' on entity '%s'", fileName.c_str(), name.c_str() ); } af.Start(); af.GetPhysics()->Rotate( spawnAxis.ToRotation() ); af.GetPhysics()->Translate( spawnOrigin ); LoadState( spawnArgs ); af.UpdateAnimation(); animator.CreateFrame( gameLocal.time, true ); UpdateVisuals(); return true; } /* ================ idAFEntity_Base::Think ================ */ void idAFEntity_Base::Think( void ) { RunPhysics(); UpdateAnimation(); if ( thinkFlags & TH_UPDATEVISUALS ) { Present(); LinkCombat(); } // RAVEN BEGIN // kfuller: added if (spawnArgs.GetBool("touchtriggers")) { TouchTriggers(); } // rjohnson: added check to see if we no longer need to think! if (GetPhysics()->IsAtRest() && !animator.IsAnimating( gameLocal.time, true ) ) { BecomeInactive( TH_ANIMATE ); } // RAVEN END } /* ================ idAFEntity_Base::BodyForClipModelId ================ */ int idAFEntity_Base::BodyForClipModelId( int id ) const { return af.BodyForClipModelId( id ); } /* ================ idAFEntity_Base::SaveState ================ */ void idAFEntity_Base::SaveState( idDict &args ) const { const idKeyValue *kv; // save the ragdoll pose af.SaveState( args ); // save all the bind constraints kv = spawnArgs.MatchPrefix( "bindConstraint ", NULL ); while ( kv ) { args.Set( kv->GetKey(), kv->GetValue() ); kv = spawnArgs.MatchPrefix( "bindConstraint ", kv ); } // save the bind if it exists kv = spawnArgs.FindKey( "bind" ); if ( kv ) { args.Set( kv->GetKey(), kv->GetValue() ); } kv = spawnArgs.FindKey( "bindToJoint" ); if ( kv ) { args.Set( kv->GetKey(), kv->GetValue() ); } kv = spawnArgs.FindKey( "bindToBody" ); if ( kv ) { args.Set( kv->GetKey(), kv->GetValue() ); } } /* ================ idAFEntity_Base::LoadState ================ */ void idAFEntity_Base::LoadState( const idDict &args ) { af.LoadState( args ); } /* ================ idAFEntity_Base::AddBindConstraints ================ */ void idAFEntity_Base::AddBindConstraints( void ) { af.AddBindConstraints(); } /* ================ idAFEntity_Base::RemoveBindConstraints ================ */ void idAFEntity_Base::RemoveBindConstraints( void ) { af.RemoveBindConstraints(); } /* ================ idAFEntity_Base::GetImpactInfo ================ */ void idAFEntity_Base::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) { if ( af.IsActive() ) { af.GetImpactInfo( ent, id, point, info ); } else { idEntity::GetImpactInfo( ent, id, point, info ); } } /* ================ idAFEntity_Base::ApplyImpulse ================ */ void idAFEntity_Base::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) { if ( !splash && ( noPlayerImpactFX || GetNoPlayerImpactFX( ) ) ) { if ( ent->IsType( idPlayer::GetClassType() ) ) {//player return; } if ( ent->IsType( idProjectile::GetClassType() ) ) {//projectile if ( ((idProjectile*)ent)->GetOwner() && ((idProjectile*)ent)->GetOwner()->IsType( idPlayer::GetClassType() ) ) {//owned by player return; } } } if ( af.IsLoaded() ) { af.ApplyImpulse( ent, id, point, impulse ); } if ( !af.IsActive() ) { idEntity::ApplyImpulse( ent, id, point, impulse ); } } /* ================ idAFEntity_Base::AddForce ================ */ void idAFEntity_Base::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) { if ( af.IsLoaded() ) { af.AddForce( ent, id, point, force ); } if ( !af.IsActive() ) { idEntity::AddForce( ent, id, point, force ); } } bool idAFEntity_Base::CanPlayImpactEffect ( idEntity* attacker, idEntity* target ) { if ( ( noPlayerImpactFX && GetNoPlayerImpactFX( ) ) && attacker && attacker->IsType( idPlayer::GetClassType() ) ) { return false; } return idAnimatedEntity::CanPlayImpactEffect( attacker, target ); } /* ================ idAFEntity_Base::Collide ================ */ bool idAFEntity_Base::Collide( const trace_t &collision, const idVec3 &velocity ) { float v, f; if ( af.IsActive() ) { v = -( velocity * collision.c.normal ); if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) { // RAVEN BEGIN // jscott: fixed negative sqrt call if( v > BOUNCE_SOUND_MAX_VELOCITY ) { f = 1.0f; } else if( v <= BOUNCE_SOUND_MIN_VELOCITY ) { f = 0.0f; } else { f = ( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / ( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) ); } // RAVEN END if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) { // don't set the volume unless there is a bounce sound as it overrides the entire channel // which causes footsteps on ai's to not honor their shader parms SetSoundVolume( f ); } nextSoundTime = gameLocal.time + 500; } } return false; } /* ================ idAFEntity_Base::GetPhysicsToVisualTransform ================ */ bool idAFEntity_Base::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) { if ( af.IsActive() ) { af.GetPhysicsToVisualTransform( origin, axis ); return true; } return idEntity::GetPhysicsToVisualTransform( origin, axis ); } /* ================ idAFEntity_Base::UpdateAnimationControllers ================ */ bool idAFEntity_Base::UpdateAnimationControllers( void ) { if ( af.IsActive() ) { if ( af.UpdateAnimation() ) { return true; } } return false; } /* ================ idAFEntity_Base::SetCombatModel ================ */ void idAFEntity_Base::SetCombatModel( void ) { if ( combatModel ) { combatModel->Unlink(); combatModel->LoadModel( modelDefHandle ); } else { // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END combatModel = new idClipModel( modelDefHandle ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END } } /* ================ idAFEntity_Base::GetCombatModel ================ */ idClipModel *idAFEntity_Base::GetCombatModel( void ) const { return combatModel; } /* ================ idAFEntity_Base::SetCombatContents ================ */ void idAFEntity_Base::SetCombatContents( bool enable ) { assert( combatModel ); if ( enable && combatModelContents ) { assert( !combatModel->GetContents() ); combatModel->SetContents( combatModelContents ); combatModelContents = 0; } else if ( !enable && combatModel->GetContents() ) { assert( !combatModelContents ); combatModelContents = combatModel->GetContents(); combatModel->SetContents( 0 ); } } /* ================ idAFEntity_Base::LinkCombat ================ */ void idAFEntity_Base::LinkCombat( void ) { if ( fl.hidden ) { return; } if ( combatModel ) { // RAVEN BEGIN // ddynerman: multiple clip worlds combatModel->Link( this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle ); // RAVEN END } } /* ================ idAFEntity_Base::UnlinkCombat ================ */ void idAFEntity_Base::UnlinkCombat( void ) { if ( combatModel ) { combatModel->Unlink(); } } /* ================ idAFEntity_Base::FreeModelDef ================ */ void idAFEntity_Base::FreeModelDef( void ) { UnlinkCombat(); idEntity::FreeModelDef(); } /* =============== idAFEntity_Base::ShowEditingDialog =============== */ void idAFEntity_Base::ShowEditingDialog( void ) { common->InitTool( EDITOR_AF, &spawnArgs ); } /* ================ idAFEntity_Base::DropAFs The entity should have the following key/value pairs set: "def_dropAF" "af def" "dropSkin" "skin name" To drop multiple articulated figures the following key/value pairs can be used: "def_dropAF*" "af def" where * is an aribtrary string. ================ */ void idAFEntity_Base::DropAFs( idEntity *ent, const char *type, idList *list ) { const idKeyValue *kv; const char *skinName; idEntity *newEnt; idAFEntity_Base *af; idDict args; const idDeclSkin *skin; // drop the articulated figures kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), NULL ); while ( kv ) { args.Set( "classname", kv->GetValue() ); gameLocal.SpawnEntityDef( args, &newEnt ); if ( newEnt && newEnt->IsType( idAFEntity_Base::Type ) ) { af = static_cast(newEnt); af->GetPhysics()->SetOrigin( ent->GetPhysics()->GetOrigin() ); af->GetPhysics()->SetAxis( ent->GetPhysics()->GetAxis() ); af->af.SetupPose( ent, gameLocal.time ); if ( list ) { list->Append( af ); } } kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), kv ); } // change the skin to hide all the dropped articulated figures skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) ); if ( skinName[0] ) { skin = declManager->FindSkin( skinName ); ent->SetSkin( skin ); } } /* ================ idAFEntity_Base::GetNoPlayerImpactFX ================ */ bool idAFEntity_Base::GetNoPlayerImpactFX( void ) { if ( GetTeamMaster( ) && this != GetTeamMaster( ) && GetTeamMaster( )->IsType( idAFEntity_Base::GetClassType( ) ) ) { return static_cast( GetTeamMaster( ) )->GetNoPlayerImpactFX( ); } else { return noPlayerImpactFX; } } /* ================ idAFEntity_Base::Event_SetConstraintPosition ================ */ void idAFEntity_Base::Event_SetConstraintPosition( const char *name, const idVec3 &pos ) { af.SetConstraintPosition( name, pos ); } /* =============================================================================== idAFEntity_Gibbable =============================================================================== */ const idEventDef EV_Gib( "gib", "s" ); const idEventDef EV_Gibbed( "" ); CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Gibbable ) EVENT( EV_Gib, idAFEntity_Gibbable::Event_Gib ) EVENT( EV_Gibbed, idAFEntity_Base::Event_Remove ) END_CLASS /* ================ idAFEntity_Gibbable::idAFEntity_Gibbable ================ */ idAFEntity_Gibbable::idAFEntity_Gibbable( void ) { skeletonModel = NULL; skeletonModelDefHandle = -1; gibbed = false; } /* ================ idAFEntity_Gibbable::~idAFEntity_Gibbable ================ */ idAFEntity_Gibbable::~idAFEntity_Gibbable() { if ( skeletonModelDefHandle != -1 ) { gameRenderWorld->FreeEntityDef( skeletonModelDefHandle ); skeletonModelDefHandle = -1; } } /* ================ idAFEntity_Gibbable::Save ================ */ void idAFEntity_Gibbable::Save( idSaveGame *savefile ) const { savefile->WriteBool( gibbed ); savefile->WriteBool( combatModel != NULL ); } /* ================ idAFEntity_Gibbable::Restore ================ */ void idAFEntity_Gibbable::Restore( idRestoreGame *savefile ) { bool hasCombatModel; savefile->ReadBool( gibbed ); savefile->ReadBool( hasCombatModel ); InitSkeletonModel(); if ( hasCombatModel ) { SetCombatModel(); LinkCombat(); } // precache decls declManager->FindType( DECL_ENTITYDEF, "damage_Gib", false, false ); } /* ================ idAFEntity_Gibbable::Spawn ================ */ void idAFEntity_Gibbable::Spawn( void ) { InitSkeletonModel(); gibbed = false; // precache decls declManager->FindType( DECL_ENTITYDEF, "damage_Gib", false, false ); } /* ================ idAFEntity_Gibbable::InitSkeletonModel ================ */ void idAFEntity_Gibbable::InitSkeletonModel( void ) { const char *modelName; const idDeclModelDef *modelDef; skeletonModel = NULL; skeletonModelDefHandle = -1; modelName = spawnArgs.GetString( "model_gib" ); modelDef = NULL; if ( modelName[0] != '\0' ) { modelDef = static_cast( declManager->FindType( DECL_MODELDEF, modelName, false ) ); if ( modelDef ) { skeletonModel = modelDef->ModelHandle(); } else { skeletonModel = renderModelManager->FindModel( modelName ); } if ( skeletonModel != NULL && renderEntity.hModel != NULL ) { if ( skeletonModel->NumJoints() != renderEntity.hModel->NumJoints() ) { gameLocal.Error( "gib model '%s' has different number of joints than model '%s'", skeletonModel->Name(), renderEntity.hModel->Name() ); } } } } /* ================ idAFEntity_Gibbable::Present ================ */ void idAFEntity_Gibbable::Present( void ) { renderEntity_t skeleton; if ( !gameLocal.isNewFrame ) { return; } // don't present to the renderer if the entity hasn't changed if ( !( thinkFlags & TH_UPDATEVISUALS ) ) { return; } // update skeleton model if ( gibbed && !IsHidden() && skeletonModel != NULL ) { skeleton = renderEntity; skeleton.hModel = skeletonModel; // add to refresh list if ( skeletonModelDefHandle == -1 ) { skeletonModelDefHandle = gameRenderWorld->AddEntityDef( &skeleton ); } else { gameRenderWorld->UpdateEntityDef( skeletonModelDefHandle, &skeleton ); } } idEntity::Present(); } /* ================ idAFEntity_Gibbable::Damage ================ */ void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) { if ( !fl.takedamage ) { return; } idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location ); if ( health < -20 && spawnArgs.GetBool( "gib" ) ) { Gib( dir, damageDefName ); } } /* ===================== idAFEntity_Gibbable::SpawnGibs ===================== */ void idAFEntity_Gibbable::SpawnGibs( const idVec3 &dir, const char *damageDefName ) { int i; bool gibNonSolid; idVec3 entityCenter, velocity; idList list; assert( !gameLocal.isClient ); // RAVEN BEGIN // ddynerman: added false as 2nd parameter, otherwise def will be created const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName, false ); // RAVEN END if ( !damageDef ) { gameLocal.Error( "Unknown damageDef '%s'", damageDefName ); } // spawn gib articulated figures idAFEntity_Base::DropAFs( this, "gib", &list ); // spawn gib items idMoveableItem::DropItems( this, "gib", &list ); // blow out the gibs in the given direction away from the center of the entity entityCenter = GetPhysics()->GetAbsBounds().GetCenter(); gibNonSolid = damageDef->GetBool( "gibNonSolid" ); for ( i = 0; i < list.Num(); i++ ) { if ( gibNonSolid ) { list[i]->GetPhysics()->SetContents( 0 ); list[i]->GetPhysics()->SetClipMask( 0 ); list[i]->GetPhysics()->UnlinkClip(); list[i]->GetPhysics()->PutToRest(); } else { list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE ); list[i]->GetPhysics()->SetClipMask( CONTENTS_SOLID ); velocity = list[i]->GetPhysics()->GetAbsBounds().GetCenter() - entityCenter; velocity.NormalizeFast(); velocity += ( i & 1 ) ? dir : -dir; list[i]->GetPhysics()->SetLinearVelocity( velocity * 225.0f ); } list[i]->GetRenderEntity()->noShadow = true; list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f; // RAVEN BEGIN // dluetscher: reduced the gib lifetime from 4. to 1.5 list[i]->PostEventSec( &EV_Remove, 1.5f ); // RAVEN END } } /* ============ idAFEntity_Gibbable::Gib ============ */ void idAFEntity_Gibbable::Gib( const idVec3 &dir, const char *damageDefName ) { // only gib once if ( gibbed ) { return; } // RAVEN BEGIN // ddynerman: added false as 2nd parameter, otherwise def will be created const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName, false ); // RAVEN END if ( !damageDef ) { gameLocal.Error( "Unknown damageDef '%s'", damageDefName ); } if ( damageDef->GetBool( "gibNonSolid" ) ) { GetAFPhysics()->SetContents( 0 ); GetAFPhysics()->SetClipMask( 0 ); GetAFPhysics()->UnlinkClip(); GetAFPhysics()->PutToRest(); } else { GetAFPhysics()->SetContents( CONTENTS_CORPSE ); GetAFPhysics()->SetClipMask( CONTENTS_SOLID ); } UnlinkCombat(); // RAVEN BEGIN // mekberg: changed from g_bloodEffects to g_decals if ( g_decals.GetBool() ) { // RAVEN END if ( gameLocal.time > gameLocal.GetGibTime() ) { gameLocal.SetGibTime( gameLocal.time + GIB_DELAY ); SpawnGibs( dir, damageDefName ); renderEntity.noShadow = true; renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f; StartSound( "snd_gibbed", SND_CHANNEL_ANY, 0, false, NULL ); gibbed = true; } } else { gibbed = true; } // RAVEN BEGIN // bdube: default is to remove the character immediately // however, this is bad in the case of the player. What was happening is that the // player was being removed right away, but the respawn logic runs off of // idPlayer::EvaluateControls, which no longer gets run when the player is gibbed and removed. // So...the game effectively sits locked on a black screen and you never get a menu.. if ( !gameLocal.isMultiplayer && this->IsType( idPlayer::GetClassType() )) { return; } PostEventSec( &EV_Gibbed, spawnArgs.GetFloat ( "gibRemoveDelay", "0" ) ); // RAVEN END } /* ============ idAFEntity_Gibbable::Event_Gib ============ */ void idAFEntity_Gibbable::Event_Gib( const char *damageDefName ) { Gib( idVec3( 0, 0, 1 ), damageDefName ); } /* =============================================================================== idAFEntity_Generic =============================================================================== */ CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_Generic ) EVENT( EV_Activate, idAFEntity_Generic::Event_Activate ) END_CLASS /* ================ idAFEntity_Generic::idAFEntity_Generic ================ */ idAFEntity_Generic::idAFEntity_Generic( void ) { keepRunningPhysics = false; } /* ================ idAFEntity_Generic::~idAFEntity_Generic ================ */ idAFEntity_Generic::~idAFEntity_Generic( void ) { } /* ================ idAFEntity_Generic::Save ================ */ void idAFEntity_Generic::Save( idSaveGame *savefile ) const { savefile->WriteBool( keepRunningPhysics ); } /* ================ idAFEntity_Generic::Restore ================ */ void idAFEntity_Generic::Restore( idRestoreGame *savefile ) { savefile->ReadBool( keepRunningPhysics ); } /* ================ idAFEntity_Generic::Think ================ */ void idAFEntity_Generic::Think( void ) { idAFEntity_Base::Think(); if ( keepRunningPhysics ) { BecomeActive( TH_PHYSICS ); } } /* ================ idAFEntity_Generic::Spawn ================ */ void idAFEntity_Generic::Spawn( void ) { if ( !LoadAF() ) { gameLocal.Error( "Couldn't load af file on entity '%s'", name.c_str() ); } SetCombatModel(); SetPhysics( af.GetPhysics() ); // RAVEN BEGIN // kfuller: we just put the guy to rest so don't be affected by an attractor /* const char *attractor = spawnArgs.GetString("attractor"); if (attractor && attractor[0]) { rvRagdollAttractor *attractorEnt = dynamic_cast(gameLocal.FindEntity(attractor)); if (attractorEnt) { attractorEnt->RemoveAttractee(entityNumber); } else { gameLocal.Warning("idAFEntity::LoadAF -- failed to find attractor '%s'", attractor); } } */ // RAVEN END af.GetPhysics()->PutToRest(); if ( !spawnArgs.GetBool( "nodrop", "0" ) ) { af.GetPhysics()->Activate(); } fl.takedamage = true; } /* ================ idAFEntity_Generic::Event_Activate ================ */ void idAFEntity_Generic::Event_Activate( idEntity *activator ) { float delay; idVec3 init_velocity, init_avelocity; Show(); af.GetPhysics()->EnableImpact(); af.GetPhysics()->Activate(); spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity ); spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity ); delay = spawnArgs.GetFloat( "init_velocityDelay", "0" ); if ( delay == 0.0f ) { af.GetPhysics()->SetLinearVelocity( init_velocity ); } else { PostEventSec( &EV_SetLinearVelocity, delay, init_velocity ); } delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" ); if ( delay == 0.0f ) { af.GetPhysics()->SetAngularVelocity( init_avelocity ); } else { PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity ); } } /* =============================================================================== idAFEntity_WithAttachedHead =============================================================================== */ CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_WithAttachedHead ) EVENT( EV_Gib, idAFEntity_WithAttachedHead::Event_Gib ) EVENT( EV_Activate, idAFEntity_WithAttachedHead::Event_Activate ) END_CLASS /* ================ idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead ================ */ idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead() { head = NULL; } /* ================ idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead ================ */ idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead() { if ( head.GetEntity() ) { head.GetEntity()->ClearBody(); head.GetEntity()->PostEventMS( &EV_Remove, 0 ); } } /* ================ idAFEntity_WithAttachedHead::Spawn ================ */ void idAFEntity_WithAttachedHead::Spawn( void ) { SetupHead(); LoadAF(); SetCombatModel(); SetPhysics( af.GetPhysics() ); af.GetPhysics()->PutToRest(); if ( !spawnArgs.GetBool( "nodrop", "0" ) ) { af.GetPhysics()->Activate(); } fl.takedamage = true; if ( head.GetEntity() ) { int anim = head.GetEntity()->GetAnimator()->GetAnim( "dead" ); if ( anim ) { // RAVEN BEGIN frameBlend_t frameBlend = { 0, 0, 0, 1.0f, 0 }; head.GetEntity()->GetAnimator()->SetFrame( ANIMCHANNEL_ALL, anim, frameBlend ); // RAVEN END } } idEntity *headEnt = head.GetEntity(); idAnimator *headAnimator; if ( headEnt ) { headAnimator = headEnt->GetAnimator(); } else { headAnimator = &animator; } } /* ================ idAFEntity_WithAttachedHead::Save ================ */ void idAFEntity_WithAttachedHead::Save( idSaveGame *savefile ) const { head.Save( savefile ); } /* ================ idAFEntity_WithAttachedHead::Restore ================ */ void idAFEntity_WithAttachedHead::Restore( idRestoreGame *savefile ) { head.Restore( savefile ); } /* ================ idAFEntity_WithAttachedHead::SetupHead ================ */ void idAFEntity_WithAttachedHead::SetupHead( const char* headDefName ) { idAFAttachment *headEnt; idStr jointName; jointHandle_t joint; const idKeyValue *sndKV; if ( gameLocal.isClient && head.GetEntity() == NULL ) { return; } // If we don't pass in a specific head model, try looking it up if( !headDefName[ 0 ] ) { headDefName = spawnArgs.GetString( "def_head", "" ); } if ( headDefName[ 0 ] ) { jointName = spawnArgs.GetString( "joint_head" ); joint = animator.GetJointHandle( jointName ); if ( joint == INVALID_JOINT ) { gameLocal.Error( "Joint '%s' not found for 'joint_head' on '%s'", jointName.c_str(), name.c_str() ); } // copy any sounds in case we have frame commands on the head idDict args; sndKV = spawnArgs.MatchPrefix( "snd_", NULL ); while( sndKV ) { args.Set( sndKV->GetKey(), sndKV->GetValue() ); sndKV = spawnArgs.MatchPrefix( "snd_", sndKV ); } if ( !gameLocal.isClient ) { args.Set( "classname", headDefName ); if( !gameLocal.SpawnEntityDef( args, ( idEntity ** )&headEnt ) ) { gameLocal.Warning( "idActor::SetupHead() - Unknown head model '%s'\n", headDefName ); return; } headEnt->spawnArgs.Set( "classname", headDefName ); headEnt->SetName( va( "%s_head", name.c_str() ) ); headEnt->SetBody ( this, headDefName, joint ); head = headEnt; } else { // we got our spawnid from the server headEnt = head.GetEntity(); headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber + 1; } headEnt->BindToJoint( this, joint, true ); headEnt->GetPhysics()->SetOrigin( vec3_origin ); headEnt->GetPhysics()->SetAxis( mat3_identity ); head->InitCopyJoints ( ); } else if ( head ) { head->PostEventMS( &EV_Remove, 0 ); head = NULL; } } /* ================ idAFEntity_WithAttachedHead::Think ================ */ void idAFEntity_WithAttachedHead::Think( void ) { idAFEntity_Base::Think(); } /* ================ idAFEntity_WithAttachedHead::LinkCombat ================ */ void idAFEntity_WithAttachedHead::LinkCombat( void ) { idAFAttachment *headEnt; if ( fl.hidden ) { return; } if ( combatModel ) { // RAVEN BEGIN // ddynerman: multiple clip worlds combatModel->Link( this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle ); // RAVEN END } headEnt = head.GetEntity(); if ( headEnt ) { headEnt->LinkCombat(); } } /* ================ idAFEntity_WithAttachedHead::UnlinkCombat ================ */ void idAFEntity_WithAttachedHead::UnlinkCombat( void ) { idAFAttachment *headEnt; if ( combatModel ) { combatModel->Unlink(); } headEnt = head.GetEntity(); if ( headEnt ) { headEnt->UnlinkCombat(); } } /* ================ idAFEntity_WithAttachedHead::Hide ================ */ void idAFEntity_WithAttachedHead::Hide( void ) { idAFEntity_Base::Hide(); if ( head.GetEntity() ) { head.GetEntity()->Hide(); } UnlinkCombat(); } /* ================ idAFEntity_WithAttachedHead::Show ================ */ void idAFEntity_WithAttachedHead::Show( void ) { idAFEntity_Base::Show(); if ( head.GetEntity() ) { head.GetEntity()->Show(); } LinkCombat(); } /* ================ idAFEntity_WithAttachedHead::ProjectOverlay ================ */ void idAFEntity_WithAttachedHead::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) { idEntity::ProjectOverlay( origin, dir, size, material ); if ( head.GetEntity() ) { head.GetEntity()->ProjectOverlay( origin, dir, size, material ); } } /* ============ idAFEntity_WithAttachedHead::Gib ============ */ void idAFEntity_WithAttachedHead::Gib( const idVec3 &dir, const char *damageDefName ) { // only gib once if ( gibbed ) { return; } idAFEntity_Gibbable::Gib( dir, damageDefName ); if ( head.GetEntity() ) { head.GetEntity()->Hide(); } } /* ============ idAFEntity_WithAttachedHead::Event_Gib ============ */ void idAFEntity_WithAttachedHead::Event_Gib( const char *damageDefName ) { Gib( idVec3( 0, 0, 1 ), damageDefName ); } /* ================ idAFEntity_WithAttachedHead::Event_Activate ================ */ void idAFEntity_WithAttachedHead::Event_Activate( idEntity *activator ) { float delay; idVec3 init_velocity, init_avelocity; Show(); af.GetPhysics()->EnableImpact(); af.GetPhysics()->Activate(); spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity ); spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity ); delay = spawnArgs.GetFloat( "init_velocityDelay", "0" ); if ( delay == 0.0f ) { af.GetPhysics()->SetLinearVelocity( init_velocity ); } else { PostEventSec( &EV_SetLinearVelocity, delay, init_velocity ); } delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" ); if ( delay == 0.0f ) { af.GetPhysics()->SetAngularVelocity( init_avelocity ); } else { PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity ); } } /* =============================================================================== idAFEntity_Vehicle =============================================================================== */ CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Vehicle ) END_CLASS /* ================ idAFEntity_Vehicle::idAFEntity_Vehicle ================ */ idAFEntity_Vehicle::idAFEntity_Vehicle( void ) { player = NULL; eyesJoint = INVALID_JOINT; steeringWheelJoint = INVALID_JOINT; wheelRadius = 0.0f; steerAngle = 0.0f; steerSpeed = 0.0f; // dustSmoke = NULL; } /* ================ idAFEntity_Vehicle::Spawn ================ */ void idAFEntity_Vehicle::Spawn( void ) { const char *eyesJointName = spawnArgs.GetString( "eyesJoint", "eyes" ); const char *steeringWheelJointName = spawnArgs.GetString( "steeringWheelJoint", "steeringWheel" ); LoadAF(); SetCombatModel(); SetPhysics( af.GetPhysics() ); fl.takedamage = true; if ( !eyesJointName[0] ) { gameLocal.Error( "idAFEntity_Vehicle '%s' no eyes joint specified", name.c_str() ); } eyesJoint = animator.GetJointHandle( eyesJointName ); if ( !steeringWheelJointName[0] ) { gameLocal.Error( "idAFEntity_Vehicle '%s' no steering wheel joint specified", name.c_str() ); } steeringWheelJoint = animator.GetJointHandle( steeringWheelJointName ); spawnArgs.GetFloat( "wheelRadius", "20", wheelRadius ); spawnArgs.GetFloat( "steerSpeed", "5", steerSpeed ); player = NULL; steerAngle = 0.0f; /* const char *smokeName = spawnArgs.GetString( "smoke_vehicle_dust", "muzzlesmoke" ); if ( *smokeName != '\0' ) { dustSmoke = static_cast( declManager->FindType( DECL_PARTICLE, smokeName ) ); } */ } /* ================ idAFEntity_Vehicle::Use ================ */ void idAFEntity_Vehicle::Use( idPlayer *other ) { idVec3 origin; idMat3 axis; if ( player ) { if ( player == other ) { other->Unbind(); player = NULL; af.GetPhysics()->SetComeToRest( true ); } } else { player = other; animator.GetJointTransform( eyesJoint, gameLocal.time, origin, axis ); origin = renderEntity.origin + origin * renderEntity.axis; player->GetPhysics()->SetOrigin( origin ); player->BindToBody( this, 0, true ); af.GetPhysics()->SetComeToRest( false ); af.GetPhysics()->Activate(); } } /* ================ idAFEntity_Vehicle::GetSteerAngle ================ */ float idAFEntity_Vehicle::GetSteerAngle( void ) { float idealSteerAngle, angleDelta; idealSteerAngle = player->usercmd.rightmove * ( 30.0f / 128.0f ); angleDelta = idealSteerAngle - steerAngle; if ( angleDelta > steerSpeed ) { steerAngle += steerSpeed; } else if ( angleDelta < -steerSpeed ) { steerAngle -= steerSpeed; } else { steerAngle = idealSteerAngle; } return steerAngle; } /* =============================================================================== idAFEntity_VehicleFourWheels =============================================================================== */ CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleFourWheels ) END_CLASS /* ================ idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels ================ */ idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels( void ) { int i; for ( i = 0; i < 4; i++ ) { wheels[i] = NULL; wheelJoints[i] = INVALID_JOINT; wheelAngles[i] = 0.0f; } steering[0] = NULL; steering[1] = NULL; } /* ================ idAFEntity_VehicleFourWheels::Spawn ================ */ void idAFEntity_VehicleFourWheels::Spawn( void ) { int i; static const char *wheelBodyKeys[] = { "wheelBodyFrontLeft", "wheelBodyFrontRight", "wheelBodyRearLeft", "wheelBodyRearRight" }; static const char *wheelJointKeys[] = { "wheelJointFrontLeft", "wheelJointFrontRight", "wheelJointRearLeft", "wheelJointRearRight" }; static const char *steeringHingeKeys[] = { "steeringHingeFrontLeft", "steeringHingeFrontRight", }; const char *wheelBodyName, *wheelJointName, *steeringHingeName; for ( i = 0; i < 4; i++ ) { wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" ); if ( !wheelBodyName[0] ) { gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] ); } wheels[i] = af.GetPhysics()->GetBody( wheelBodyName ); if ( !wheels[i] ) { gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName ); } wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" ); if ( !wheelJointName[0] ) { gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] ); } wheelJoints[i] = animator.GetJointHandle( wheelJointName ); if ( wheelJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName ); } } for ( i = 0; i < 2; i++ ) { steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" ); if ( !steeringHingeName[0] ) { gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] ); } steering[i] = static_cast(af.GetPhysics()->GetConstraint( steeringHingeName )); if ( !steering[i] ) { gameLocal.Error( "idAFEntity_VehicleFourWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName ); } } memset( wheelAngles, 0, sizeof( wheelAngles ) ); BecomeActive( TH_THINK ); } /* ================ idAFEntity_VehicleFourWheels::Think ================ */ void idAFEntity_VehicleFourWheels::Think( void ) { int i; float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f; idVec3 origin; idMat3 axis; idRotation rotation; if ( thinkFlags & TH_THINK ) { if ( player ) { // capture the input from a player velocity = g_vehicleVelocity.GetFloat(); if ( player->usercmd.forwardmove < 0 ) { velocity = -velocity; } force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f); steerAngle = GetSteerAngle(); } // update the wheel motor force for ( i = 0; i < 2; i++ ) { wheels[2+i]->SetContactMotorVelocity( velocity ); wheels[2+i]->SetContactMotorForce( force ); } // adjust wheel velocity for better steering because there are no differentials between the wheels if ( steerAngle < 0.0f ) { wheels[2]->SetContactMotorVelocity( velocity * 0.5f ); } else if ( steerAngle > 0.0f ) { wheels[3]->SetContactMotorVelocity( velocity * 0.5f ); } // update the wheel steering steering[0]->SetSteerAngle( steerAngle ); steering[1]->SetSteerAngle( steerAngle ); for ( i = 0; i < 2; i++ ) { steering[i]->SetSteerSpeed( 3.0f ); } // update the steering wheel animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis ); rotation.SetVec( axis[2] ); rotation.SetAngle( -steerAngle ); animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() ); // run the physics RunPhysics(); // rotate the wheels visually for ( i = 0; i < 4; i++ ) { if ( force == 0.0f ) { velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0]; } // RAVEN BEGIN // bdube: msec to GetMsec wheelAngles[i] += velocity * MS2SEC( gameLocal.GetMSec() ) / wheelRadius; // RAVEN END // give the wheel joint an additional rotation about the wheel axis rotation.SetAngle( RAD2DEG( wheelAngles[i] ) ); axis = af.GetPhysics()->GetAxis( 0 ); rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] ); animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() ); } } UpdateAnimation(); if ( thinkFlags & TH_UPDATEVISUALS ) { Present(); LinkCombat(); } } /* =============================================================================== idAFEntity_VehicleSixWheels =============================================================================== */ CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSixWheels ) END_CLASS /* ================ idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels ================ */ idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels( void ) { int i; for ( i = 0; i < 6; i++ ) { wheels[i] = NULL; wheelJoints[i] = INVALID_JOINT; wheelAngles[i] = 0.0f; } steering[0] = NULL; steering[1] = NULL; steering[2] = NULL; steering[3] = NULL; } /* ================ idAFEntity_VehicleSixWheels::Spawn ================ */ void idAFEntity_VehicleSixWheels::Spawn( void ) { int i; static const char *wheelBodyKeys[] = { "wheelBodyFrontLeft", "wheelBodyFrontRight", "wheelBodyMiddleLeft", "wheelBodyMiddleRight", "wheelBodyRearLeft", "wheelBodyRearRight" }; static const char *wheelJointKeys[] = { "wheelJointFrontLeft", "wheelJointFrontRight", "wheelJointMiddleLeft", "wheelJointMiddleRight", "wheelJointRearLeft", "wheelJointRearRight" }; static const char *steeringHingeKeys[] = { "steeringHingeFrontLeft", "steeringHingeFrontRight", "steeringHingeRearLeft", "steeringHingeRearRight" }; const char *wheelBodyName, *wheelJointName, *steeringHingeName; for ( i = 0; i < 6; i++ ) { wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" ); if ( !wheelBodyName[0] ) { gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] ); } wheels[i] = af.GetPhysics()->GetBody( wheelBodyName ); if ( !wheels[i] ) { gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName ); } wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" ); if ( !wheelJointName[0] ) { gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] ); } wheelJoints[i] = animator.GetJointHandle( wheelJointName ); if ( wheelJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName ); } } for ( i = 0; i < 4; i++ ) { steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" ); if ( !steeringHingeName[0] ) { gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] ); } steering[i] = static_cast(af.GetPhysics()->GetConstraint( steeringHingeName )); if ( !steering[i] ) { gameLocal.Error( "idAFEntity_VehicleSixWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName ); } } memset( wheelAngles, 0, sizeof( wheelAngles ) ); BecomeActive( TH_THINK ); } /* ================ idAFEntity_VehicleSixWheels::Think ================ */ void idAFEntity_VehicleSixWheels::Think( void ) { int i; float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f; idVec3 origin; idMat3 axis; idRotation rotation; if ( thinkFlags & TH_THINK ) { if ( player ) { // capture the input from a player velocity = g_vehicleVelocity.GetFloat(); if ( player->usercmd.forwardmove < 0 ) { velocity = -velocity; } force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f); steerAngle = GetSteerAngle(); } // update the wheel motor force for ( i = 0; i < 6; i++ ) { wheels[i]->SetContactMotorVelocity( velocity ); wheels[i]->SetContactMotorForce( force ); } // adjust wheel velocity for better steering because there are no differentials between the wheels if ( steerAngle < 0.0f ) { for ( i = 0; i < 3; i++ ) { wheels[(i<<1)]->SetContactMotorVelocity( velocity * 0.5f ); } } else if ( steerAngle > 0.0f ) { for ( i = 0; i < 3; i++ ) { wheels[1+(i<<1)]->SetContactMotorVelocity( velocity * 0.5f ); } } // update the wheel steering steering[0]->SetSteerAngle( steerAngle ); steering[1]->SetSteerAngle( steerAngle ); steering[2]->SetSteerAngle( -steerAngle ); steering[3]->SetSteerAngle( -steerAngle ); for ( i = 0; i < 4; i++ ) { steering[i]->SetSteerSpeed( 3.0f ); } // update the steering wheel animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis ); rotation.SetVec( axis[2] ); rotation.SetAngle( -steerAngle ); animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() ); // run the physics RunPhysics(); // rotate the wheels visually for ( i = 0; i < 6; i++ ) { if ( force == 0.0f ) { velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0]; } // RAVEN BEGIN // bdube: msec to GetMsec wheelAngles[i] += velocity * MS2SEC( gameLocal.GetMSec() ) / wheelRadius; // RAVEN END // give the wheel joint an additional rotation about the wheel axis rotation.SetAngle( RAD2DEG( wheelAngles[i] ) ); axis = af.GetPhysics()->GetAxis( 0 ); rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] ); animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() ); } } UpdateAnimation(); if ( thinkFlags & TH_UPDATEVISUALS ) { Present(); LinkCombat(); } } /* =============================================================================== idAFEntity_SteamPipe =============================================================================== */ CLASS_DECLARATION( idAFEntity_Base, idAFEntity_SteamPipe ) END_CLASS /* ================ idAFEntity_SteamPipe::idAFEntity_SteamPipe ================ */ idAFEntity_SteamPipe::idAFEntity_SteamPipe( void ) { steamBody = 0; steamForce = 0.0f; steamUpForce = 0.0f; steamModelDefHandle = -1; memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) ); } /* ================ idAFEntity_SteamPipe::~idAFEntity_SteamPipe ================ */ idAFEntity_SteamPipe::~idAFEntity_SteamPipe( void ) { if ( steamModelDefHandle >= 0 ){ gameRenderWorld->FreeEntityDef( steamModelDefHandle ); } } /* ================ idAFEntity_SteamPipe::Save ================ */ void idAFEntity_SteamPipe::Save( idSaveGame *savefile ) const { } /* ================ idAFEntity_SteamPipe::Restore ================ */ void idAFEntity_SteamPipe::Restore( idRestoreGame *savefile ) { Spawn(); } /* ================ idAFEntity_SteamPipe::Spawn ================ */ void idAFEntity_SteamPipe::Spawn( void ) { idVec3 steamDir; const char *steamBodyName; LoadAF(); SetCombatModel(); SetPhysics( af.GetPhysics() ); fl.takedamage = true; steamBodyName = spawnArgs.GetString( "steamBody", "" ); steamForce = spawnArgs.GetFloat( "steamForce", "2000" ); steamUpForce = spawnArgs.GetFloat( "steamUpForce", "10" ); steamDir = af.GetPhysics()->GetAxis( steamBody )[2]; steamBody = af.GetPhysics()->GetBodyId( steamBodyName ); force.SetPosition( af.GetPhysics(), steamBody, af.GetPhysics()->GetOrigin( steamBody ) ); force.SetForce( steamDir * -steamForce ); InitSteamRenderEntity(); BecomeActive( TH_THINK ); } /* ================ idAFEntity_SteamPipe::InitSteamRenderEntity ================ */ void idAFEntity_SteamPipe::InitSteamRenderEntity( void ) { const char *temp; const idDeclModelDef *modelDef; memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) ); steamRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f; steamRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f; steamRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f; modelDef = NULL; temp = spawnArgs.GetString ( "model_steam" ); if ( *temp != '\0' ) { if ( !strstr( temp, "." ) ) { modelDef = static_cast( declManager->FindType( DECL_MODELDEF, temp, false ) ); if ( modelDef ) { steamRenderEntity.hModel = modelDef->ModelHandle(); } } if ( !steamRenderEntity.hModel ) { steamRenderEntity.hModel = renderModelManager->FindModel( temp ); } if ( steamRenderEntity.hModel ) { steamRenderEntity.bounds = steamRenderEntity.hModel->Bounds( &steamRenderEntity ); } else { steamRenderEntity.bounds.Zero(); } steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody ); steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody ); steamModelDefHandle = gameRenderWorld->AddEntityDef( &steamRenderEntity ); } } /* ================ idAFEntity_SteamPipe::Think ================ */ void idAFEntity_SteamPipe::Think( void ) { idVec3 steamDir; if ( thinkFlags & TH_THINK ) { steamDir.x = gameLocal.random.CRandomFloat() * steamForce; steamDir.y = gameLocal.random.CRandomFloat() * steamForce; steamDir.z = steamUpForce; force.SetForce( steamDir ); force.Evaluate( gameLocal.time ); //gameRenderWorld->DebugArrow( colorWhite, af.GetPhysics()->GetOrigin( steamBody ), af.GetPhysics()->GetOrigin( steamBody ) - 10.0f * steamDir, 4 ); } if ( steamModelDefHandle >= 0 ){ steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody ); steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody ); gameRenderWorld->UpdateEntityDef( steamModelDefHandle, &steamRenderEntity ); } idAFEntity_Base::Think(); } /* =============================================================================== idAFEntity_ClawFourFingers =============================================================================== */ const idEventDef EV_SetFingerAngle( "setFingerAngle", "f" ); const idEventDef EV_StopFingers( "stopFingers" ); CLASS_DECLARATION( idAFEntity_Base, idAFEntity_ClawFourFingers ) EVENT( EV_SetFingerAngle, idAFEntity_ClawFourFingers::Event_SetFingerAngle ) EVENT( EV_StopFingers, idAFEntity_ClawFourFingers::Event_StopFingers ) END_CLASS static const char *clawConstraintNames[] = { "claw1", "claw2", "claw3", "claw4" }; /* ================ idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers ================ */ idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers( void ) { fingers[0] = NULL; fingers[1] = NULL; fingers[2] = NULL; fingers[3] = NULL; } /* ================ idAFEntity_ClawFourFingers::Save ================ */ void idAFEntity_ClawFourFingers::Save( idSaveGame *savefile ) const { int i; for ( i = 0; i < 4; i++ ) { fingers[i]->Save( savefile ); } } /* ================ idAFEntity_ClawFourFingers::Restore ================ */ void idAFEntity_ClawFourFingers::Restore( idRestoreGame *savefile ) { int i; for ( i = 0; i < 4; i++ ) { fingers[i] = static_cast(af.GetPhysics()->GetConstraint( clawConstraintNames[i] )); fingers[i]->Restore( savefile ); } SetCombatModel(); LinkCombat(); } /* ================ idAFEntity_ClawFourFingers::Spawn ================ */ void idAFEntity_ClawFourFingers::Spawn( void ) { int i; LoadAF(); SetCombatModel(); af.GetPhysics()->LockWorldConstraints( true ); af.GetPhysics()->SetForcePushable( true ); SetPhysics( af.GetPhysics() ); fl.takedamage = true; for ( i = 0; i < 4; i++ ) { fingers[i] = static_cast(af.GetPhysics()->GetConstraint( clawConstraintNames[i] )); if ( !fingers[i] ) { gameLocal.Error( "idClaw_FourFingers '%s': can't find claw constraint '%s'", name.c_str(), clawConstraintNames[i] ); } } } /* ================ idAFEntity_ClawFourFingers::Event_SetFingerAngle ================ */ void idAFEntity_ClawFourFingers::Event_SetFingerAngle( float angle ) { int i; for ( i = 0; i < 4; i++ ) { fingers[i]->SetSteerAngle( angle ); fingers[i]->SetSteerSpeed( 0.5f ); } af.GetPhysics()->Activate(); } /* ================ idAFEntity_ClawFourFingers::Event_StopFingers ================ */ void idAFEntity_ClawFourFingers::Event_StopFingers( void ) { int i; for ( i = 0; i < 4; i++ ) { fingers[i]->SetSteerAngle( fingers[i]->GetAngle() ); } } /* =============================================================================== editor support routines =============================================================================== */ /* ================ idGameEdit::AF_SpawnEntity ================ */ bool idGameEdit::AF_SpawnEntity( const char *fileName ) { idDict args; idPlayer *player; idAFEntity_Generic *ent; const idDeclAF *af; idVec3 org; float yaw; player = gameLocal.GetLocalPlayer(); if ( !player || !gameLocal.CheatsOk( false ) ) { return false; } af = static_cast( declManager->FindType( DECL_AF, fileName ) ); if ( !af ) { return false; } yaw = player->viewAngles.yaw; args.Set( "angle", va( "%f", yaw + 180 ) ); org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 ); args.Set( "origin", org.ToString() ); args.Set( "spawnclass", "idAFEntity_Generic" ); if ( af->model[0] ) { args.Set( "model", af->model.c_str() ); } else { args.Set( "model", fileName ); } if ( af->skin[0] ) { args.Set( "skin", af->skin.c_str() ); } args.Set( "articulatedFigure", fileName ); args.Set( "nodrop", "1" ); // RAVEN BEGIN // jnewquist: Use accessor for static class type ent = static_cast(gameLocal.SpawnEntityType( idAFEntity_Generic::GetClassType(), &args)); // RAVEN END // always update this entity ent->BecomeActive( TH_THINK ); ent->KeepRunningPhysics(); ent->fl.forcePhysicsUpdate = true; player->dragEntity.SetSelected( ent ); return true; } /* ================ idGameEdit::AF_UpdateEntities ================ */ void idGameEdit::AF_UpdateEntities( const char *fileName ) { idEntity *ent; idAFEntity_Base *af; idStr name; name = fileName; name.StripFileExtension(); // reload any idAFEntity_Generic which uses the given articulated figure file for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( ent->IsType( idAFEntity_Base::GetClassType() ) ) { // RAVEN END af = static_cast(ent); if ( name.Icmp( af->GetAFName() ) == 0 ) { af->LoadAF(); af->GetAFPhysics()->PutToRest(); } } } } /* ================ idGameEdit::AF_UndoChanges ================ */ void idGameEdit::AF_UndoChanges( void ) { int i, c; idEntity *ent; idAFEntity_Base *af; idDeclAF *decl; c = declManager->GetNumDecls( DECL_AF ); for ( i = 0; i < c; i++ ) { decl = static_cast( const_cast( declManager->DeclByIndex( DECL_AF, i, false ) ) ); if ( !decl->modified ) { continue; } decl->Invalidate(); declManager->FindType( DECL_AF, decl->GetName() ); // reload all AF entities using the file for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( ent->IsType( idAFEntity_Base::GetClassType() ) ) { // RAVEN END af = static_cast(ent); if ( idStr::Icmp( decl->GetName(), af->GetAFName() ) == 0 ) { af->LoadAF(); } } } } } /* ================ GetJointTransform ================ */ typedef struct { renderEntity_t *ent; const idMD5Joint *joints; } jointTransformData_t; static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) { int i; jointTransformData_t *data = reinterpret_cast(model); for ( i = 0; i < data->ent->numJoints; i++ ) { if ( data->joints[i].name.Icmp( jointName ) == 0 ) { break; } } if ( i >= data->ent->numJoints ) { return false; } origin = frame[i].ToVec3(); axis = frame[i].ToMat3(); return true; } /* ================ GetArgString ================ */ static const char *GetArgString( const idDict &args, const idDict *defArgs, const char *key ) { const char *s; s = args.GetString( key ); if ( !s[0] && defArgs ) { s = defArgs->GetString( key ); } return s; } /* ================ idGameEdit::AF_CreateMesh ================ */ idRenderModel *idGameEdit::AF_CreateMesh( const idDict &args, idVec3 &meshOrigin, idMat3 &meshAxis, bool &poseIsSet ) { int i, jointNum; const idDeclAF *af; const idDeclAF_Body *fb = NULL; renderEntity_t ent; idVec3 origin, *bodyOrigin, *newBodyOrigin, *modifiedOrigin; idMat3 axis, *bodyAxis, *newBodyAxis, *modifiedAxis; declAFJointMod_t *jointMod; idAngles angles; const idDict *defArgs; const idKeyValue *arg; idStr name; jointTransformData_t data; const char *classname, *afName, *modelName; idRenderModel *md5; const idDeclModelDef *modelDef; const idMD5Anim *MD5anim; const idMD5Joint *MD5joint; const idMD5Joint *MD5joints; int numMD5joints; idJointMat *originalJoints; int parentNum; poseIsSet = false; meshOrigin.Zero(); meshAxis.Identity(); classname = args.GetString( "classname" ); defArgs = gameLocal.FindEntityDefDict( classname ); // get the articulated figure afName = GetArgString( args, defArgs, "articulatedFigure" ); af = static_cast( declManager->FindType( DECL_AF, afName ) ); if ( !af ) { return NULL; } // get the md5 model modelName = GetArgString( args, defArgs, "model" ); modelDef = static_cast< const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) ); if ( !modelDef ) { return NULL; } // make sure model hasn't been purged if ( modelDef->ModelHandle() && !modelDef->ModelHandle()->IsLoaded() ) { modelDef->ModelHandle()->LoadModel(); } // get the md5 md5 = modelDef->ModelHandle(); if ( !md5 || md5->IsDefaultModel() ) { return NULL; } // get the articulated figure pose anim int animNum = modelDef->GetAnim( "af_pose" ); if ( !animNum ) { return NULL; } const idAnim *anim = modelDef->GetAnim( animNum ); if ( !anim ) { return NULL; } MD5anim = anim->MD5Anim( 0 ); MD5joints = md5->GetJoints(); numMD5joints = md5->NumJoints(); // setup a render entity memset( &ent, 0, sizeof( ent ) ); ent.customSkin = modelDef->GetSkin(); ent.bounds.Clear(); ent.numJoints = numMD5joints; ent.joints = ( idJointMat * )_alloca16( ent.numJoints * sizeof( *ent.joints ) ); // create animation from of the af_pose ANIM_CreateAnimFrame( md5, MD5anim, ent.numJoints, ent.joints, 1, modelDef->GetVisualOffset(), false ); // buffers to store the initial origin and axis for each body bodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) ); bodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) ); newBodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) ); newBodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) ); // finish the AF positions data.ent = &ent; data.joints = MD5joints; af->Finish( GetJointTransform, ent.joints, &data ); // get the initial origin and axis for each AF body for ( i = 0; i < af->bodies.Num(); i++ ) { fb = af->bodies[i]; if ( fb->modelType == TRM_BONE ) { // axis of bone trace model axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3(); axis[2].Normalize(); axis[2].NormalVectors( axis[0], axis[1] ); axis[1] = -axis[1]; } else { axis = fb->angles.ToMat3(); } newBodyOrigin[i] = bodyOrigin[i] = fb->origin.ToVec3(); newBodyAxis[i] = bodyAxis[i] = axis; } // get any new body transforms stored in the key/value pairs for ( arg = args.MatchPrefix( "body ", NULL ); arg; arg = args.MatchPrefix( "body ", arg ) ) { name = arg->GetKey(); name.Strip( "body " ); for ( i = 0; i < af->bodies.Num(); i++ ) { fb = af->bodies[i]; if ( fb->name.Icmp( name ) == 0 ) { break; } } if ( i >= af->bodies.Num() ) { continue; } sscanf( arg->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll ); if ( fb->jointName.Icmp( "origin" ) == 0 ) { meshAxis = bodyAxis[i].Transpose() * angles.ToMat3(); meshOrigin = origin - bodyOrigin[i] * meshAxis; poseIsSet = true; } else { newBodyOrigin[i] = origin; newBodyAxis[i] = angles.ToMat3(); } } // save the original joints originalJoints = ( idJointMat * )_alloca16( numMD5joints * sizeof( originalJoints[0] ) ); // RAVEN BEGIN // JSinger: Changed to call optimized memcpy SIMDProcessor->Memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) ); // RAVEN END // buffer to store the joint mods jointMod = (declAFJointMod_t *) _alloca16( numMD5joints * sizeof( declAFJointMod_t ) ); memset( jointMod, -1, numMD5joints * sizeof( declAFJointMod_t ) ); modifiedOrigin = (idVec3 *) _alloca16( numMD5joints * sizeof( idVec3 ) ); memset( modifiedOrigin, 0, numMD5joints * sizeof( idVec3 ) ); modifiedAxis = (idMat3 *) _alloca16( numMD5joints * sizeof( idMat3 ) ); memset( modifiedAxis, 0, numMD5joints * sizeof( idMat3 ) ); // get all the joint modifications for ( i = 0; i < af->bodies.Num(); i++ ) { fb = af->bodies[i]; if ( fb->jointName.Icmp( "origin" ) == 0 ) { continue; } for ( jointNum = 0; jointNum < numMD5joints; jointNum++ ) { if ( MD5joints[jointNum].name.Icmp( fb->jointName ) == 0 ) { break; } } if ( jointNum >= 0 && jointNum < ent.numJoints ) { jointMod[ jointNum ] = fb->jointMod; modifiedAxis[ jointNum ] = ( bodyAxis[i] * originalJoints[jointNum].ToMat3().Transpose() ).Transpose() * ( newBodyAxis[i] * meshAxis.Transpose() ); // FIXME: calculate correct modifiedOrigin modifiedOrigin[ jointNum ] = originalJoints[ jointNum ].ToVec3(); } } // apply joint modifications to the skeleton MD5joint = MD5joints + 1; for( i = 1; i < numMD5joints; i++, MD5joint++ ) { parentNum = MD5joint->parent - MD5joints; idMat3 parentAxis = originalJoints[ parentNum ].ToMat3(); idMat3 localm = originalJoints[i].ToMat3() * parentAxis.Transpose(); idVec3 localt = ( originalJoints[i].ToVec3() - originalJoints[ parentNum ].ToVec3() ) * parentAxis.Transpose(); switch( jointMod[i] ) { case DECLAF_JOINTMOD_ORIGIN: { ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() ); ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] ); break; } case DECLAF_JOINTMOD_AXIS: { ent.joints[ i ].SetRotation( modifiedAxis[ i ] ); ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() ); break; } case DECLAF_JOINTMOD_BOTH: { ent.joints[ i ].SetRotation( modifiedAxis[ i ] ); ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] ); break; } default: { ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() ); ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() ); break; } } } // instantiate a mesh using the joint information from the render entity return md5->InstantiateDynamicModel( &ent, NULL, NULL ); } // RAVEN BEGIN // bdube: af attractors /* =============================================================================== rvAFAttractor =============================================================================== */ CLASS_DECLARATION( idEntity, rvAFAttractor ) END_CLASS // RAVEN END