quake4-sdk/source/game/client/ClientMoveable.cpp
2007-06-15 00:00:00 +00:00

379 lines
11 KiB
C++

//----------------------------------------------------------------
// ClientMoveable.cpp
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
/*
===============================================================================
rvClientMoveable
===============================================================================
*/
const idEventDef CL_FadeOut( "<fadeOut>", "d" );
const idEventDef CL_ClearDepthHack ( "<clearDepthHack>" );
static const float BOUNCE_SOUND_MIN_VELOCITY = 100.0f;
static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
static const int BOUNCE_SOUND_DELAY = 200;
CLASS_DECLARATION( rvClientEntity, rvClientMoveable )
EVENT( CL_FadeOut, rvClientMoveable::Event_FadeOut )
EVENT( CL_ClearDepthHack, rvClientMoveable::Event_ClearDepthHack )
END_CLASS
/*
================
rvClientMoveable::rvClientMoveable
================
*/
rvClientMoveable::rvClientMoveable ( void ) {
memset ( &renderEntity, 0, sizeof(renderEntity) );
entityDefHandle = -1;
scale.Init( 0, 0, 1.0f, 1.0f );
}
/*
================
rvClientMoveable::~rvClientMoveable
================
*/
rvClientMoveable::~rvClientMoveable ( void ) {
FreeEntityDef ( );
// Remove any trail effect if there is one
if ( trailEffect ) {
trailEffect->Stop ( );
}
}
/*
================
rvClientMoveable::FreeEntityDef
================
*/
void rvClientMoveable::FreeEntityDef ( void ) {
if ( entityDefHandle >= 0 ) {
gameRenderWorld->FreeEntityDef ( entityDefHandle );
entityDefHandle = -1;
}
}
idVec3 simpleTri[3] =
{
idVec3( -1.0, -1.0, 0.0 ),
idVec3( 0.0, 2.0, 0.0 ),
idVec3( 2.0, 0.0, 0.0 )
};
/*
================
rvClientMoveable::Spawn
================
*/
void rvClientMoveable::Spawn ( void ) {
// parse static models the same way the editor display does
gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
idTraceModel trm;
int clipShrink;
idStr clipModelName;
// check if a clip model is set
spawnArgs.GetString( "clipmodel", "", clipModelName );
if ( !clipModelName.Length () ) {
clipModelName = spawnArgs.GetString( "model" ); // use the visual model
}
if ( clipModelName == SIMPLE_TRI_NAME ) {
trm.SetupPolygon( simpleTri, 3 );
} else {
clipModelName.BackSlashesToSlashes();
if ( !collisionModelManager->TrmFromModel( gameLocal.GetMapName(), clipModelName, trm ) ) {
gameLocal.Error( "rvClientMoveable '%d': cannot load collision model %s", entityNumber, clipModelName.c_str() );
return;
}
}
// if the model should be shrunk
clipShrink = spawnArgs.GetInt( "clipshrink" );
if ( clipShrink != 0 ) {
trm.Shrink( clipShrink * CM_CLIP_EPSILON );
}
physicsObj.SetSelf ( gameLocal.entities[ENTITYNUM_CLIENT] );
physicsObj.SetClipModel ( new idClipModel( trm ), spawnArgs.GetFloat ( "density", "0.5" ), entityNumber );
physicsObj.SetOrigin( GetOrigin() );
physicsObj.SetAxis( GetAxis() );
physicsObj.SetBouncyness( spawnArgs.GetFloat( "bouncyness", "0.6" ) );
physicsObj.SetFriction( spawnArgs.GetFloat("linear_friction", "0.6"), spawnArgs.GetFloat( "angular_friction", "0.6"), spawnArgs.GetFloat("friction", "0.05") );
physicsObj.SetGravity( gameLocal.GetCurrentGravity(this) );
physicsObj.SetContents( 0 );
// abahr: changed to MASK_SHOT_RENDERMODEL because brass was getting pinched between the player and the wall in some cases
// may want to try something cheaper.
physicsObj.SetClipMask( CONTENTS_OPAQUE ); // MASK_SHOT_RENDERMODEL | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP | CONTENTS_WATER );
physicsObj.Activate ( );
trailEffect = gameLocal.PlayEffect ( spawnArgs, "fx_trail", physicsObj.GetCenterMass(), GetAxis(), true );
trailAttenuateSpeed = spawnArgs.GetFloat ( "trailAttenuateSpeed", "200" );
bounceSoundShader = declManager->FindSound ( spawnArgs.GetString ( "snd_bounce" ), false );
bounceSoundTime = 0;
mPlayBounceSoundOnce = spawnArgs.GetBool("bounce_sound_once");
mHasBounced = false;
scale.Init( gameLocal.GetTime(), SEC2MS(spawnArgs.GetFloat("scale_reset_duration", "0.2")), Max(VECTOR_EPSILON, spawnArgs.GetFloat("scale", "1.0f")), 1.0f );
}
/*
================
rvClientMoveable::SetOrigin
================
*/
void rvClientMoveable::SetOrigin( const idVec3& origin ) {
rvClientEntity::SetOrigin( origin );
physicsObj.SetOrigin( origin );
}
/*
================
rvClientMoveable::SetAxis
================
*/
void rvClientMoveable::SetAxis( const idMat3& axis ) {
rvClientEntity::SetAxis( axis );
physicsObj.SetAxis( axis );
}
/*
================
rvClientMoveable::SetOwner
================
*/
void rvClientMoveable::SetOwner( idEntity* owner ) {
physicsObj.GetClipModel()->SetOwner( owner );
physicsObj.GetClipModel()->SetEntity( owner );
}
/*
================
rvClientMoveable::Think
================
*/
void rvClientMoveable::Think ( void ) {
if( bindMaster && (bindMaster->GetRenderEntity()->hModel && bindMaster->GetModelDefHandle() == -1) ) {
return;
}
RunPhysics ( );
// Special case the sound update to use the center mass since the origin may be in an odd place
idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if ( emitter ) {
refSound.origin = worldOrigin;
refSound.velocity = worldVelocity;
emitter->UpdateEmitter( refSound.origin, refSound.velocity, refSound.listenerId, &refSound.parms );
}
// Keep the trail effect following
if ( trailEffect ) {
float speed;
speed = idMath::ClampFloat ( 0, trailAttenuateSpeed, worldVelocity.LengthFast ( ) );
if ( physicsObj.IsAtRest ( ) ) {
trailEffect->Stop ( );
trailEffect = NULL;
} else {
trailEffect->SetOrigin ( worldOrigin );
trailEffect->SetAxis ( worldAxis );
trailEffect->Attenuate ( speed / trailAttenuateSpeed );
}
}
renderEntity.origin = worldOrigin;
renderEntity.axis = worldAxis * scale.GetCurrentValue( gameLocal.GetTime() );
// add to refresh list
if ( entityDefHandle == -1 ) {
entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
} else {
gameRenderWorld->UpdateEntityDef( entityDefHandle, &renderEntity );
}
}
/*
================
rvClientMoveable::GetPhysics
================
*/
idPhysics* rvClientMoveable::GetPhysics ( void ) const {
return (idPhysics*)&physicsObj;
}
/*
================
rvClientMoveable::Collide
================
*/
bool rvClientMoveable::Collide ( const trace_t &collision, const idVec3 &velocity ) {
if (mPlayBounceSoundOnce && mHasBounced)
{
return false;
}
if ( bounceSoundShader && gameLocal.time > bounceSoundTime ) {
float speed;
speed = velocity.LengthFast ( );
if ( speed > BOUNCE_SOUND_MIN_VELOCITY ) {
StartSoundShader ( bounceSoundShader, SND_CHANNEL_BODY, 0 );
bounceSoundTime = BOUNCE_SOUND_DELAY;
mHasBounced = true;
}
}
return false;
}
/*
================
rvClientMoveable::Save
================
*/
void rvClientMoveable::Save( idSaveGame *savefile ) const {
savefile->WriteRenderEntity( renderEntity );
savefile->WriteInt( entityDefHandle );
trailEffect.Save( savefile );
savefile->WriteFloat( trailAttenuateSpeed );
savefile->WriteStaticObject( physicsObj );
savefile->WriteInt( bounceSoundTime );
savefile->WriteSoundShader( bounceSoundShader );
savefile->WriteBool(mPlayBounceSoundOnce);
savefile->WriteBool(mHasBounced);
// TOSAVE: idInterpolate<float> scale;
}
/*
================
rvClientMoveable::Restore
================
*/
void rvClientMoveable::Restore( idRestoreGame *savefile ) {
savefile->ReadRenderEntity( renderEntity, NULL );
savefile->ReadInt( entityDefHandle );
trailEffect.Restore( savefile );
savefile->ReadFloat( trailAttenuateSpeed );
savefile->ReadStaticObject( physicsObj );
savefile->ReadInt( bounceSoundTime );
savefile->ReadSoundShader( bounceSoundShader );
savefile->ReadBool(mPlayBounceSoundOnce);
savefile->ReadBool(mHasBounced);
// restore must retrieve entityDefHandle from the renderer
if ( entityDefHandle != -1 ) {
entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
}
// TORESTORE: idInterpolate<float> scale;
}
/*
================
rvClientMoveable::Event_FadeOut
================
*/
void rvClientMoveable::Event_FadeOut ( int duration ) {
renderEntity.noShadow = true;
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
PostEventMS ( &EV_Remove, duration );
}
/*
================
rvClientMoveable::Event_ClearDepthHack
================
*/
void rvClientMoveable::Event_ClearDepthHack ( void ) {
renderEntity.weaponDepthHackInViewID = 0;
}
/*
================
rvClientMoveable::SpawnClientMoveables
================
*/
void rvClientMoveable::SpawnClientMoveables( idEntity* ent, const char *type, idList<rvClientMoveable *>* list ) {
const idKeyValue *kv;
idVec3 origin;
idMat3 axis;
if( list == NULL || type == NULL ) {
return;
}
// drop all items
kv = ent->spawnArgs.MatchPrefix( va( "def_%s", type ), NULL );
while ( kv ) {
origin = ent->GetPhysics()->GetOrigin();
axis = ent->GetPhysics()->GetAxis();
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if( ent->IsType( idAnimatedEntity::GetClassType() ) ) {
// RAVEN END
idAnimatedEntity* animEnt = static_cast<idAnimatedEntity*>(ent);
jointHandle_t clientMoveableJoint;
const char* clientMoveableJointName = ent->spawnArgs.GetString( va( "%s_joint", kv->GetKey().c_str() + 4 ) );
// use a joint if specified
if ( idStr::Icmp( clientMoveableJointName, "") ) {
clientMoveableJoint = animEnt->GetAnimator()->GetJointHandle( clientMoveableJointName );
if ( !animEnt->GetJointWorldTransform( clientMoveableJoint, gameLocal.time, origin, axis ) ) {
gameLocal.Warning( "%s refers to invalid joint '%s' on entity '%s'\n", va( "%s_joint", kv->GetKey().c_str() + 4 ), clientMoveableJointName, ent->name.c_str() );
origin = ent->GetPhysics()->GetOrigin();
axis = ent->GetPhysics()->GetAxis();
}
}
}
// spawn the entity
const idDict* entityDef = gameLocal.FindEntityDefDict ( kv->GetValue().c_str(), false );
if ( entityDef == NULL ) {
gameLocal.Warning( "%s refers to invalid entity def '%s' on entity '%s'\n", kv->GetKey().c_str(), kv->GetValue().c_str(), ent->name.c_str() );
break;
}
rvClientMoveable* newModel = NULL;
// force spawnclass to rvClientMoveable
gameLocal.SpawnClientEntityDef( *entityDef, (rvClientEntity**)(&newModel), false, "rvClientMoveable" );
if( !newModel ) {
gameLocal.Warning( "error spawning client moveable (invalid entity def '%s' on entity '%s')\n", kv->GetValue().c_str(), ent->name.c_str() );
break;
}
newModel->SetOrigin ( origin );
newModel->SetAxis( axis );
list->Append( newModel );
kv = ent->spawnArgs.MatchPrefix( va( "def_%s", type ), kv );
}
}