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

652 lines
15 KiB
C++

//----------------------------------------------------------------
// ClientEntity.cpp
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
ABSTRACT_DECLARATION( idClass, rvClientEntity )
END_CLASS
/*
================
rvClientEntity::rvClientEntity
================
*/
rvClientEntity::rvClientEntity( void ) {
bindMaster = NULL;
entityNumber = -1;
bindOrigin.Zero();
bindAxis.Identity();
bindJoint = INVALID_JOINT;
bindOrientated = false;
memset( &refSound, 0, sizeof(refSound) );
refSound.referenceSoundHandle = -1;
}
/*
================
rvClientEntity::~rvClientEntity
================
*/
rvClientEntity::~rvClientEntity( void ) {
Unbind();
gameLocal.UnregisterClientEntity( this );
// Free sound emitter
soundSystem->FreeSoundEmitter( SOUNDWORLD_GAME, refSound.referenceSoundHandle, true );
refSound.referenceSoundHandle = -1;
}
/*
================
rvClientEntity::Spawn
================
*/
void rvClientEntity::Spawn( void ) {
idVec3 origin;
idMat3 axis;
gameLocal.RegisterClientEntity( this );
spawnNode.SetOwner( this );
bindNode.SetOwner( this );
origin = spawnArgs.GetVector( "origin", "0 0 0" );
axis = spawnArgs.GetMatrix( "axis", "1 0 0 0 1 0 0 0 1" );
InitDefaultPhysics( origin, axis );
SetOrigin( origin );
SetAxis( axis );
}
/*
================
rvClientEntity::Present
================
*/
void rvClientEntity::Present ( void ) {
}
/*
================
rvClientEntity::FreeEntityDef
================
*/
void rvClientEntity::FreeEntityDef ( void ) {
}
/*
================
rvClientEntity::Think
================
*/
void rvClientEntity::Think ( void ) {
UpdateBind();
UpdateSound();
Present();
}
/*
================
rvClientEntity::Bind
================
*/
void rvClientEntity::Bind ( idEntity* master, jointHandle_t joint, bool isOrientated ) {
Unbind();
if ( joint != INVALID_JOINT && !dynamic_cast<idAnimatedEntity*>(master) ) {
gameLocal.Warning( "rvClientEntity::Bind: entity '%s' cannot support skeletal models.", master->GetName() );
joint = INVALID_JOINT;
}
bindMaster = master;
bindJoint = joint;
bindOrigin = worldOrigin;
bindAxis = worldAxis;
bindNode.AddToEnd ( bindMaster->clientEntities );
bindOrientated = isOrientated;
if( physics ) {
physics->SetMaster( bindMaster, bindOrientated );
}
UpdateBind();
}
/*
================
rvClientEntity::Bind
================
*/
void rvClientEntity::Unbind ( void ) {
if ( !bindMaster ) {
return;
}
bindMaster = NULL;
bindNode.Remove ( );
}
/*
================
rvClientEntity::SetOrigin
================
*/
void rvClientEntity::SetOrigin( const idVec3& origin ) {
if ( bindMaster ) {
bindOrigin = origin;
} else {
worldOrigin = origin;
}
}
/*
================
rvClientEntity::SetAxis
================
*/
void rvClientEntity::SetAxis( const idMat3& axis ) {
if ( bindMaster ) {
bindAxis = axis * bindMaster->GetRenderEntity()->axis.Transpose();
} else {
worldAxis = axis;
}
}
/*
================
rvClientEntity::UpdateBind
================
*/
void rvClientEntity::UpdateBind ( void ) {
if ( !bindMaster ) {
return;
}
if ( bindJoint != INVALID_JOINT ) {
static_cast<idAnimatedEntity*>(bindMaster.GetEntity())->GetJointWorldTransform ( bindJoint, gameLocal.time, worldOrigin, worldAxis );
} else {
bindMaster->GetPosition( worldOrigin, worldAxis );
//if ( !bindMaster->GetPhysicsToVisualTransform( worldOrigin, worldAxis ) ) {
// bindMaster->GetPosition( worldOrigin, worldAxis );
//}
}
worldOrigin += (bindOrigin * worldAxis);
worldAxis = bindAxis * worldAxis;
}
/*
================
rvClientEntity::IsClient
================
*/
bool rvClientEntity::IsClient ( void ) const {
return true;
}
/*
================
rvClientEntity::DrawDebugInfo
================
*/
void rvClientEntity::DrawDebugInfo ( void ) const {
idBounds bounds ( idVec3(-8,-8,-8), idVec3(8,8,8) );
gameRenderWorld->DebugBounds ( colorGreen, bounds, worldOrigin );
if ( gameLocal.GetLocalPlayer() ) {
gameRenderWorld->DrawText ( GetClassname ( ), worldOrigin, 0.1f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
}
}
/*
================
rvClientEntity::UpdateSound
================
*/
void rvClientEntity::UpdateSound( void ) {
idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if ( emitter ) {
refSound.origin = worldOrigin;
refSound.velocity = vec3_origin;
emitter->UpdateEmitter( refSound.origin, refSound.velocity, refSound.listenerId, &refSound.parms );
}
}
/*
================
rvClientEntity::SetSoundVolume
================
*/
void rvClientEntity::SetSoundVolume( float volume ) {
refSound.parms.volume = volume;
}
/*
================
rvClientEntity::StartSound
================
*/
bool rvClientEntity::StartSound( const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length ) {
const idSoundShader *shader;
const char *sound;
if ( length ) {
*length = 0;
}
idStr soundNameStr = soundName;
if( soundNameStr.CmpPrefix( "snd_" ) && soundNameStr.CmpPrefix( "lipsync_" ) ) {
common->Warning( "Non precached sound \'%s\'", soundName );
}
if ( !spawnArgs.GetString( soundName, "", &sound ) ) {
return false;
}
if ( *sound == '\0' ) {
return false;
}
if ( !gameLocal.isNewFrame ) {
// don't play the sound, but don't report an error
return true;
}
shader = declManager->FindSound( sound );
return StartSoundShader( shader, channel, soundShaderFlags );
}
/*
================
rvClientEntity::StartSoundShader
================
*/
int rvClientEntity::StartSoundShader ( const idSoundShader* shader, const s_channelType channel, int soundShaderFlags ) {
if ( !shader ) {
return 0;
}
idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if ( !emitter ) {
refSound.referenceSoundHandle = soundSystem->AllocSoundEmitter( SOUNDWORLD_GAME );
}
UpdateSound();
emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if( !emitter ) {
return( 0 );
}
emitter->UpdateEmitter( refSound.origin, refSound.velocity, refSound.listenerId, &refSound.parms );
return emitter->StartSound( shader, channel, gameLocal.random.RandomFloat(), soundShaderFlags );
}
/*
================
rvClientEntity::Size
Returns Returns memory size of an rvClientEntity.
================
*/
size_t rvClientEntity::Size ( void ) const {
return sizeof( rvClientEntity );
}
/*
================
rvClientEntity::Save
================
*/
void rvClientEntity::Save( idSaveGame *savefile ) const {
savefile->WriteInt( entityNumber );
// idLinkList<rvClientEntity> spawnNode; - reconstructed in the master entity load
// idLinkList<rvClientEntity> bindNode; - reconstructed in the master entity load
savefile->WriteVec3( worldOrigin );
savefile->WriteVec3( worldVelocity );
savefile->WriteMat3( worldAxis );
bindMaster.Save( savefile );
savefile->WriteVec3( bindOrigin );
savefile->WriteMat3( bindAxis );
savefile->WriteJoint( bindJoint );
savefile->WriteRefSound( refSound );
}
/*
================
rvClientEntity::Restore
================
*/
void rvClientEntity::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( entityNumber );
// idLinkList<rvClientEntity> spawnNode; - reconstructed in the master entity load
// idLinkList<rvClientEntity> bindNode; - reconstructed in the master entity load
savefile->ReadVec3( worldOrigin );
savefile->ReadVec3( worldVelocity );
savefile->ReadMat3( worldAxis );
bindMaster.Restore( savefile );
savefile->ReadVec3( bindOrigin );
savefile->ReadMat3( bindAxis );
savefile->ReadJoint( bindJoint );
savefile->ReadRefSound( refSound );
}
/*
================
rvClientEntity::RunPhysics
================
*/
void rvClientEntity::RunPhysics ( void ) {
idPhysics* physics = GetPhysics( );
if( !physics ) {
return;
}
rvClientPhysics* clientPhysics = (rvClientPhysics*)gameLocal.entities[ENTITYNUM_CLIENT];
static_cast<rvClientPhysics*>( clientPhysics )->currentEntityNumber = entityNumber;
// order important: 1) set client physics bind master to client ent's bind master
// 2) set physics to client ent's physics, which sets physics
// master to client ent's master
// 3) set client physics origin to client ent origin, depends on
// proper bind master from 1
clientPhysics->PushBindInfo( bindMaster, bindJoint, bindOrientated );
clientPhysics->SetPhysics( physics );
clientPhysics->PushOriginInfo( bindMaster ? bindOrigin : worldOrigin, bindMaster ? bindAxis : worldAxis );
physics->Evaluate ( gameLocal.time - gameLocal.previousTime, gameLocal.time );
worldOrigin = physics->GetOrigin();
worldVelocity = physics->GetLinearVelocity();
worldAxis = physics->GetAxis();
// order important: 1) restore previous bind master
// 2) reset physics with previous bind master
// 3) reset origin with previous bind master
clientPhysics->PopBindInfo();
clientPhysics->SetPhysics( NULL );
clientPhysics->PopOriginInfo();
UpdateAnimationControllers();
}
/*
================
rvClientEntity::GetPhysics
================
*/
idPhysics* rvClientEntity::GetPhysics ( void ) const {
return physics;
}
/*
================
rvClientEntity::Collide
================
*/
bool rvClientEntity::Collide ( const trace_t &collision, const idVec3 &velocity ) {
return false;
}
/*
================
rvClientEntity::GetImpactInfo
================
*/
void rvClientEntity::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
GetPhysics()->GetImpactInfo( id, point, info );
}
/*
================
rvClientEntity::ApplyImpulse
================
*/
void rvClientEntity::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) {
GetPhysics()->ApplyImpulse( id, point, impulse );
}
/*
================
rvClientEntity::AddForce
================
*/
void rvClientEntity::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
GetPhysics()->AddForce( id, point, force );
}
/*
================
rvClientEntity::UpdateAnimationControllers
================
*/
bool rvClientEntity::UpdateAnimationControllers( void ) {
return false;
}
/*
================
rvClientEntity::InitDefaultPhysics
================
*/
void rvClientEntity::InitDefaultPhysics( const idVec3 &origin, const idMat3 &axis ) {
const char *temp;
idClipModel *clipModel = NULL;
// check if a clipmodel key/value pair is set
if ( spawnArgs.GetString( "clipmodel", "", &temp ) ) {
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
clipModel = new idClipModel( temp );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
}
if ( !spawnArgs.GetBool( "noclipmodel", "0" ) ) {
// check if mins/maxs or size key/value pairs are set
if ( !clipModel ) {
idVec3 size;
idBounds bounds;
bool setClipModel = false;
if ( spawnArgs.GetVector( "mins", NULL, bounds[0] ) &&
spawnArgs.GetVector( "maxs", NULL, bounds[1] ) ) {
setClipModel = true;
if ( bounds[0][0] > bounds[1][0] || bounds[0][1] > bounds[1][1] || bounds[0][2] > bounds[1][2] ) {
gameLocal.Error( "Invalid bounds '%s'-'%s' on client entity '%d'", bounds[0].ToString(), bounds[1].ToString(), entityNumber );
}
} else if ( spawnArgs.GetVector( "size", NULL, size ) ) {
if ( ( size.x < 0.0f ) || ( size.y < 0.0f ) || ( size.z < 0.0f ) ) {
gameLocal.Error( "Invalid size '%s' on client entity '%d'", size.ToString(), entityNumber );
}
bounds[0].Set( size.x * -0.5f, size.y * -0.5f, 0.0f );
bounds[1].Set( size.x * 0.5f, size.y * 0.5f, size.z );
setClipModel = true;
}
if ( setClipModel ) {
int numSides;
idTraceModel trm;
if ( spawnArgs.GetInt( "cylinder", "0", numSides ) && numSides > 0 ) {
trm.SetupCylinder( bounds, numSides < 3 ? 3 : numSides );
} else if ( spawnArgs.GetInt( "cone", "0", numSides ) && numSides > 0 ) {
trm.SetupCone( bounds, numSides < 3 ? 3 : numSides );
// RAVEN BEGIN
// bdube: added dodecahedron
} else if ( spawnArgs.GetInt( "dodecahedron", "0", numSides ) && numSides > 0 ) {
trm.SetupDodecahedron ( bounds );
// RAVEN END
} else {
trm.SetupBox( bounds );
}
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
clipModel = new idClipModel( trm );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
}
}
// check if the visual model can be used as collision model
if ( !clipModel ) {
temp = spawnArgs.GetString( "model" );
if ( ( temp != NULL ) && ( *temp != 0 ) ) {
// RAVEN BEGIN
// jscott:slash problems
idStr canonical = temp;
canonical.BackSlashesToSlashes();
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
clipModel = new idClipModel();
if ( !clipModel->LoadModel( canonical ) ) {
delete clipModel;
clipModel = NULL;
}
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
}
}
}
defaultPhysicsObj.SetSelf( gameLocal.entities[ENTITYNUM_CLIENT] );
defaultPhysicsObj.SetClipModel( clipModel, 1.0f );
defaultPhysicsObj.SetOrigin( origin );
defaultPhysicsObj.SetAxis( axis );
physics = &defaultPhysicsObj;
// by default no collision
physics->SetContents( 0 );
}
/*
===============================================================================
rvClientPhysics
===============================================================================
*/
CLASS_DECLARATION( idEntity, rvClientPhysics )
END_CLASS
/*
=====================
rvClientPhysics::Spawn
=====================
*/
void rvClientPhysics::Spawn( void ) {
pushedOrientated = false;
}
/*
=====================
rvClientPhysics::Collide
=====================
*/
bool rvClientPhysics::Collide( const trace_t &collision, const idVec3 &velocity ) {
assert ( currentEntityNumber >= 0 && currentEntityNumber < MAX_CENTITIES );
rvClientEntity* cent;
cent = gameLocal.clientEntities [ currentEntityNumber ];
if ( cent ) {
return cent->Collide ( collision, velocity );
}
return false;
}
/*
=====================
rvClientPhysics::PushBindInfo
=====================
*/
void rvClientPhysics::PushBindInfo( idEntity* master, jointHandle_t joint, bool orientated ) {
pushedBindJoint = joint;
pushedBindMaster = master;
pushedOrientated = fl.bindOrientated;
bindMaster = master;
bindJoint = joint;
fl.bindOrientated = orientated;
}
/*
=====================
rvClientPhysics::PopBindInfo
=====================
*/
void rvClientPhysics::PopBindInfo( void ) {
bindMaster = pushedBindMaster;
bindJoint = pushedBindJoint;
fl.bindOrientated = pushedOrientated;
}
/*
=====================
rvClientPhysics::PushOriginInfo
=====================
*/
void rvClientPhysics::PushOriginInfo( const idVec3& origin, const idMat3& axis ) {
if( !GetPhysics() ) {
return;
}
pushedOrigin = GetPhysics()->GetOrigin();
pushedAxis = GetPhysics()->GetAxis();
GetPhysics()->SetOrigin( origin );
GetPhysics()->SetAxis( axis );
}
/*
=====================
rvClientPhysics::PopOriginInfo
=====================
*/
void rvClientPhysics::PopOriginInfo( void ) {
if( !GetPhysics() ) {
return;
}
GetPhysics()->SetOrigin( pushedOrigin );
GetPhysics()->SetAxis( pushedAxis );
}