3204 lines
78 KiB
C++
3204 lines
78 KiB
C++
|
|
||
|
#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<jointModTransform_t>( 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<idAFEntity_Base*>( 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_drop<type>AF" "af def"
|
||
|
"drop<type>Skin" "skin name"
|
||
|
To drop multiple articulated figures the following key/value pairs can be used:
|
||
|
"def_drop<type>AF*" "af def"
|
||
|
where * is an aribtrary string.
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::DropAFs( idEntity *ent, const char *type, idList<idEntity *> *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<idAFEntity_Base *>(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<idAFEntity_Base*>( 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( "<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<const idDeclModelDef *>( 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<idEntity *> 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<rvRagdollAttractor*>(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<const idDeclParticle *>( 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<idAFConstraint_Hinge *>(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<idAFConstraint_Hinge *>(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<const idDeclModelDef *>( 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<idAFConstraint_Hinge *>(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<idAFConstraint_Hinge *>(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<const idDeclAF *>( 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<idAFEntity_Generic *>(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<idAFEntity_Base *>(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<idDeclAF *>( const_cast<idDecl *>( 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<idAFEntity_Base *>(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<jointTransformData_t *>(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<const idDeclAF *>( 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
|