quake4-sdk/source/mpgame/client/ClientAFEntity.cpp

697 lines
16 KiB
C++

//----------------------------------------------------------------
// ClientAFEntity.cpp
//
// Copyright 2002-2006 Raven Software
//----------------------------------------------------------------
#include "../../idlib/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();
}
}