//---------------------------------------------------------------- // ClientAFEntity.cpp // // Copyright 2002-2006 Raven Software //---------------------------------------------------------------- #include "precompiled.h" #pragma hdrstop /* =============================================================================== rvClientAFEntity =============================================================================== */ CLASS_DECLARATION( rvAnimatedClientEntity, rvClientAFEntity ) END_CLASS static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f; static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f; /* ================ rvClientAFEntity::rvClientAFEntity ================ */ rvClientAFEntity::rvClientAFEntity( void ) { combatModel = NULL; combatModelContents = 0; nextSoundTime = 0; spawnOrigin.Zero(); spawnAxis.Identity(); } /* ================ idAFEntity_Base::~idAFEntity_Base ================ */ rvClientAFEntity::~rvClientAFEntity( void ) { delete combatModel; combatModel = NULL; } /* ================ rvClientAFEntity::Spawn ================ */ void rvClientAFEntity::Spawn( void ) { spawnOrigin = worldOrigin; spawnAxis = worldAxis; nextSoundTime = 0; //if ( !LoadAF() ) { // gameLocal.Error( "Couldn't load af file on entity %d", entityNumber ); //} SetCombatModel(); //af.GetPhysics()->PutToRest(); //if ( !spawnArgs.GetBool( "nodrop", "0" ) ) { // af.GetPhysics()->Activate(); //} } /* ================ rvClientAFEntity::LoadAF ================ */ bool rvClientAFEntity::LoadAF( const char* keyname ) { idStr fileName; if ( !keyname || !*keyname ) { keyname = "articulatedFigure"; } if ( !spawnArgs.GetString( keyname, "*unknown*", fileName ) ) { return false; } af.SetAnimator( GetAnimator() ); idDict args = gameLocal.entities[ ENTITYNUM_CLIENT ]->spawnArgs; gameLocal.entities[ ENTITYNUM_CLIENT ]->spawnArgs = spawnArgs; if ( !af.Load( gameLocal.entities[ ENTITYNUM_CLIENT ], fileName ) ) { gameLocal.Error( "rvClientAFEntity::LoadAF: Couldn't load af file '%s' on client entity %d", fileName.c_str(), entityNumber ); } gameLocal.entities[ ENTITYNUM_CLIENT ]->spawnArgs = args; af.Start(); af.GetPhysics()->Rotate( spawnAxis.ToRotation() ); af.GetPhysics()->Translate( spawnOrigin ); LoadState( spawnArgs ); af.UpdateAnimation(); animator.CreateFrame( gameLocal.time, true ); UpdateVisuals(); return true; } /* ================ rvClientAFEntity::Think ================ */ void rvClientAFEntity::Think( void ) { RunPhysics(); UpdateAnimation(); Present(); LinkCombat(); } /* ================ rvClientAFEntity::BodyForClipModelId ================ */ int rvClientAFEntity::BodyForClipModelId( int id ) const { return af.BodyForClipModelId( id ); } /* ================ rvClientAFEntity::SaveState ================ */ void rvClientAFEntity::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() ); } } /* ================ rvClientAFEntity::LoadState ================ */ void rvClientAFEntity::LoadState( const idDict &args ) { af.LoadState( args ); } /* ================ rvClientAFEntity::AddBindConstraints ================ */ void rvClientAFEntity::AddBindConstraints( void ) { af.AddBindConstraints(); } /* ================ rvClientAFEntity::RemoveBindConstraints ================ */ void rvClientAFEntity::RemoveBindConstraints( void ) { af.RemoveBindConstraints(); } /* ================ rvClientAFEntity::GetImpactInfo ================ */ void rvClientAFEntity::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) { if ( af.IsActive() ) { af.GetImpactInfo( ent, id, point, info ); } else { rvClientEntity::GetImpactInfo( ent, id, point, info ); } } /* ================ rvClientAFEntity::ApplyImpulse ================ */ void rvClientAFEntity::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) { if ( af.IsLoaded() ) { af.ApplyImpulse( ent, id, point, impulse ); } if ( !af.IsActive() ) { rvClientEntity::ApplyImpulse( ent, id, point, impulse, splash ); } } /* ================ rvClientAFEntity::AddForce ================ */ void rvClientAFEntity::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) { if ( af.IsLoaded() ) { af.AddForce( ent, id, point, force ); } if ( !af.IsActive() ) { rvClientEntity::AddForce( ent, id, point, force ); } } bool rvClientAFEntity::CanPlayImpactEffect ( idEntity* attacker, idEntity* target ) { // stubbed out return true; } /* ================ rvClientAFEntity::Collide ================ */ bool rvClientAFEntity::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; } /* ================ rvClientAFEntity::GetPhysicsToVisualTransform ================ */ bool rvClientAFEntity::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) { if ( af.IsActive() ) { af.GetPhysicsToVisualTransform( origin, axis ); return true; } return rvClientModel::GetPhysicsToVisualTransform( origin, axis ); } /* ================ rvClientAFEntity::UpdateAnimationControllers ================ */ bool rvClientAFEntity::UpdateAnimationControllers( void ) { if ( af.IsActive() ) { if ( af.UpdateAnimation() ) { return true; } } return false; } /* ================ rvClientAFEntity::SetCombatModel ================ */ void rvClientAFEntity::SetCombatModel( void ) { if ( combatModel ) { combatModel->Unlink(); combatModel->LoadModel( entityDefHandle ); } else { RV_PUSH_HEAP_MEM(this); combatModel = new idClipModel( entityDefHandle ); RV_POP_HEAP(); } } /* ================ rvClientAFEntity::GetCombatModel ================ */ idClipModel *rvClientAFEntity::GetCombatModel( void ) const { return combatModel; } /* ================ rvClientAFEntity::SetCombatContents ================ */ void rvClientAFEntity::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 ); } } /* ================ rvClientAFEntity::LinkCombat ================ */ void rvClientAFEntity::LinkCombat( void ) { if ( combatModel ) { combatModel->Link( gameLocal.entities[ ENTITYNUM_CLIENT ], 0, renderEntity.origin, renderEntity.axis, entityDefHandle ); } } /* ================ rvClientAFEntity::UnlinkCombat ================ */ void rvClientAFEntity::UnlinkCombat( void ) { if ( combatModel ) { combatModel->Unlink(); } } /* ================ rvClientAFEntity::FreeEntityDef ================ */ void rvClientAFEntity::FreeEntityDef( void ) { UnlinkCombat(); rvClientEntity::FreeEntityDef(); } /* ================ rvClientAFEntity::GetNoPlayerImpactFX ================ */ bool rvClientAFEntity::GetNoPlayerImpactFX( void ) { // stubbed out return false; } /* =============================================================================== idAFAttachment =============================================================================== */ CLASS_DECLARATION( rvClientAFEntity, rvClientAFAttachment ) END_CLASS /* ===================== rvClientAFAttachment::rvClientAFAttachment ===================== */ rvClientAFAttachment::rvClientAFAttachment( void ) { body = NULL; combatModel = NULL; idleAnim = 0; damageJoint = INVALID_JOINT; } /* ===================== rvClientAFAttachment::~rvClientAFAttachment ===================== */ rvClientAFAttachment::~rvClientAFAttachment( void ) { delete combatModel; combatModel = NULL; } /* ===================== rvClientAFAttachment::Spawn ===================== */ void rvClientAFAttachment::Spawn( void ) { idleAnim = animator.GetAnim( "idle" ); } /* ===================== rvClientAFAttachment::InitCopyJoints ===================== */ void rvClientAFAttachment::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 client entity %d", jointName, entityNumber ); continue; } copyJoint.to = animator.GetJointHandle( kv->GetValue() ); if ( copyJoint.to == INVALID_JOINT ) { gameLocal.Warning( "Unknown copy_joint '%s' on head of entity %d", kv->GetValue().c_str(), entityNumber ); continue; } copyJoints.Append( copyJoint ); } } /* ===================== rvClientAFAttachment::CopyJointsFromBody ===================== */ void rvClientAFAttachment::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 ); } } } /* ===================== rvClientAFAttachment::SetBody ===================== */ void rvClientAFAttachment::SetBody( idAnimatedEntity *bodyEnt, const char *model, jointHandle_t _damageJoint ) { body = bodyEnt; damageJoint = _damageJoint; SetModel( model ); spawnArgs.SetBool( "bleed", body->spawnArgs.GetBool( "bleed" ) ); } /* ===================== rvClientAFAttachment::ClearBody ===================== */ void rvClientAFAttachment::ClearBody( void ) { body = NULL; damageJoint = INVALID_JOINT; Hide(); } /* ===================== rvClientAFAttachment::GetBody ===================== */ idEntity *rvClientAFAttachment::GetBody( void ) const { return body; } /* ================ idAFAttachment::Hide ================ */ void rvClientAFAttachment::Hide( void ) { UnlinkCombat(); } /* ================ idAFAttachment::Show ================ */ void rvClientAFAttachment::Show( void ) { LinkCombat(); } /* ================ idAFAttachment::AddDamageEffect ================ */ void rvClientAFAttachment::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 ); } } /* ================ rvClientAFAttachment::GetImpactInfo ================ */ void rvClientAFAttachment::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 { rvClientAFAttachment::GetImpactInfo( ent, id, point, info ); } } /* ================ rvClientAFAttachment::CanPlayImpactEffect ================ */ bool rvClientAFAttachment::CanPlayImpactEffect ( idEntity* attacker, idEntity* target ) { return rvClientAFEntity::CanPlayImpactEffect( attacker, target ); } /* ================ rvClientAFAttachment::ApplyImpulse ================ */ void rvClientAFAttachment::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 { rvClientAFEntity::ApplyImpulse( ent, id, point, impulse ); } } /* ================ rvClientAFAttachment::AddForce ================ */ void rvClientAFAttachment::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 { rvClientAFEntity::AddForce( ent, id, point, force ); } } /* ================ rvClientAFAttachment::PlayIdleAnim ================ */ void rvClientAFAttachment::PlayIdleAnim( int channel, int blendTime ) { if ( idleAnim && ( idleAnim != animator.CurrentAnim( channel )->AnimNum() ) ) { animator.CycleAnim( channel, idleAnim, gameLocal.time, blendTime ); } } /* ================ rvClientAFAttachment::Think ================ */ void rvClientAFAttachment::Think( void ) { rvClientAFEntity::Think(); } /* ================ rvClientAFAttachment::UpdateAnimationControllers ================ */ bool rvClientAFAttachment::UpdateAnimationControllers( void ) { CopyJointsFromBody( ); return rvClientAFEntity::UpdateAnimationControllers( ); } /* ================ rvClientAFAttachment::SetCombatModel ================ */ void rvClientAFAttachment::SetCombatModel( void ) { if ( combatModel ) { combatModel->Unlink(); combatModel->LoadModel( entityDefHandle ); } else { RV_PUSH_HEAP_MEM(this); combatModel = new idClipModel( entityDefHandle ); RV_POP_HEAP(); } combatModel->SetOwner( body ); } /* ================ rvClientAFAttachment::GetCombatModel ================ */ idClipModel *rvClientAFAttachment::GetCombatModel( void ) const { return combatModel; } /* ================ rvClientAFAttachment::LinkCombat ================ */ void rvClientAFAttachment::LinkCombat( void ) { if ( combatModel ) { combatModel->Link( gameLocal.entities[ ENTITYNUM_CLIENT ], 0, renderEntity.origin, renderEntity.axis, entityDefHandle ); } } /* ================ rvClientAFAttachment::UnlinkCombat ================ */ void rvClientAFAttachment::UnlinkCombat( void ) { if ( combatModel ) { combatModel->Unlink(); } }