quadrilateralcowboy/d3xp/AFEntity.cpp

4069 lines
100 KiB
C++
Raw Normal View History

2020-06-12 21:06:25 +00:00
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../idlib/precompiled.h"
#pragma hdrstop
#include "Game_local.h"
#define STEER_MAXANGLE 35
#define STEER_MAXANGLE_AI 55
#define HIT_WAYPOINT_THRESHOLD 200
/*
===============================================================================
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;
}
}
}
/*
================
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
clip = new idClipModel( trm );
clip->SetContents( CONTENTS_SOLID );
clip->Link( gameLocal.clip, this, 0, org, mat3_identity );
body = new idAFBody( name + idStr(i), clip, density );
physicsObj.AddBody( body );
// visual model for body
SetModelForId( physicsObj.GetBodyId( body ), spawnArgs.GetString( "model" ) );
// add constraint
if ( bindToWorld ) {
if ( !lastBody ) {
uj = new idAFConstraint_UniversalJoint( name + idStr(i), body, lastBody );
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 {
uj = new idAFConstraint_UniversalJoint( name + idStr(i), lastBody, body );
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 ) {
bsj = new idAFConstraint_BallAndSocketJoint( "joint" + idStr(i), lastBody, body );
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
===============================================================================
*/
CLASS_DECLARATION( idAnimatedEntity, idAFAttachment )
END_CLASS
/*
=====================
idAFAttachment::idAFAttachment
=====================
*/
idAFAttachment::idAFAttachment( void ) {
body = NULL;
combatModel = NULL;
idleAnim = 0;
attachJoint = INVALID_JOINT;
}
/*
=====================
idAFAttachment::~idAFAttachment
=====================
*/
idAFAttachment::~idAFAttachment( void ) {
StopSound( SND_CHANNEL_ANY, false );
delete combatModel;
combatModel = NULL;
}
/*
=====================
idAFAttachment::Spawn
=====================
*/
void idAFAttachment::Spawn( void ) {
idleAnim = animator.GetAnim( "idle" );
}
/*
=====================
idAFAttachment::SetBody
=====================
*/
void idAFAttachment::SetBody( idEntity *bodyEnt, const char *model, jointHandle_t attachJoint ) {
bool bleed;
body = bodyEnt;
this->attachJoint = attachJoint;
SetModel( model );
fl.takedamage = true;
bleed = body->spawnArgs.GetBool( "bleed" );
spawnArgs.SetBool( "bleed", bleed );
}
/*
=====================
idAFAttachment::ClearBody
=====================
*/
void idAFAttachment::ClearBody( void ) {
body = NULL;
attachJoint = 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 {
savefile->WriteObject( body );
savefile->WriteInt( idleAnim );
savefile->WriteJoint( attachJoint );
}
/*
================
idAFAttachment::Restore
unarchives object from save game file
================
*/
void idAFAttachment::Restore( idRestoreGame *savefile ) {
savefile->ReadObject( reinterpret_cast<idClass *&>( body ) );
savefile->ReadInt( idleAnim );
savefile->ReadJoint( attachJoint );
SetCombatModel();
LinkCombat();
}
/*
================
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, attachJoint );
}
}
/*
================
idAFAttachment::AddDamageEffect
================
*/
void idAFAttachment::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
if ( body ) {
trace_t c = collision;
c.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint );
body->AddDamageEffect( c, velocity, damageDefName );
}
}
/*
================
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( attachJoint ), point, info );
} else {
idEntity::GetImpactInfo( ent, id, point, info );
}
}
/*
================
idAFAttachment::ApplyImpulse
================
*/
void idAFAttachment::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
if ( body ) {
body->ApplyImpulse( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), 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( attachJoint ), point, force );
} else {
idEntity::AddForce( ent, id, point, force );
}
}
/*
================
idAFAttachment::PlayIdleAnim
================
*/
void idAFAttachment::PlayIdleAnim( int blendTime ) {
if ( idleAnim && ( idleAnim != animator.CurrentAnim( ANIMCHANNEL_ALL )->AnimNum() ) ) {
animator.CycleAnim( ANIMCHANNEL_ALL, idleAnim, gameLocal.time, blendTime );
}
}
/*
================
idAfAttachment::Think
================
*/
void idAFAttachment::Think( void ) {
idAnimatedEntity::Think();
if ( thinkFlags & TH_UPDATEPARTICLES ) {
UpdateDamageEffects();
}
}
/*
================
idAFAttachment::SetCombatModel
================
*/
void idAFAttachment::SetCombatModel( void ) {
if ( combatModel ) {
combatModel->Unlink();
combatModel->LoadModel( modelDefHandle );
} else {
combatModel = new idClipModel( modelDefHandle );
}
combatModel->SetOwner( body );
}
/*
================
idAFAttachment::GetCombatModel
================
*/
idClipModel *idAFAttachment::GetCombatModel( void ) const {
return combatModel;
}
/*
================
idAFAttachment::LinkCombat
================
*/
void idAFAttachment::LinkCombat( void ) {
if ( fl.hidden ) {
return;
}
if ( combatModel ) {
combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
}
}
/*
================
idAFAttachment::UnlinkCombat
================
*/
void idAFAttachment::UnlinkCombat( void ) {
if ( combatModel ) {
combatModel->Unlink();
}
}
/*
===============================================================================
idAFEntity_Base
===============================================================================
*/
const idEventDef EV_SetConstraintPosition( "SetConstraintPosition", "sv" );
const idEventDef EV_SetAFGravity( "setAFGravity", "v" );
const idEventDef EV_SetAFActive( "setAFActive", "d" );
const idEventDef EV_SetAFRest( "setAFRest", "d" );
CLASS_DECLARATION( idAnimatedEntity, idAFEntity_Base )
EVENT( EV_SetConstraintPosition, idAFEntity_Base::Event_SetConstraintPosition )
EVENT( EV_SetAFGravity, idAFEntity_Base::Event_SetAFGravity)
EVENT( EV_SetAFActive, idAFEntity_Base::Event_SetAFActive)
EVENT( EV_SetAFRest, idAFEntity_Base::Event_SetAFRest)
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();
}
/*
================
idAFEntity_Base::~idAFEntity_Base
================
*/
idAFEntity_Base::~idAFEntity_Base( void ) {
delete combatModel;
combatModel = NULL;
}
/*
================
idAFEntity_Base::Save
================
*/
void idAFEntity_Base::Save( idSaveGame *savefile ) const {
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->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;
}
/*
================
idAFEntity_Base::LoadAF
================
*/
bool idAFEntity_Base::LoadAF( void ) {
idStr fileName;
if ( !spawnArgs.GetString( "articulatedFigure", "*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();
}
}
/*
================
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 ) {
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 );
}
}
/*
================
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 ) {
f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) );
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 {
combatModel = new idClipModel( modelDefHandle );
}
}
/*
================
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 ) {
combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
}
}
/*
================
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::Event_SetConstraintPosition
================
*/
void idAFEntity_Base::Event_SetConstraintPosition( const char *name, const idVec3 &pos ) {
af.SetConstraintPosition( name, pos );
}
void idAFEntity_Base::Event_SetAFGravity( const idVec3 &newGravity )
{
af.GetPhysics()->SetGravity( newGravity );
}
void idAFEntity_Base::Event_SetAFRest( int value )
{
this->af.GetPhysics()->SetComeToRest( value );
if ( this->IsType( idAFEntity_VehicleSimple::Type ) )
{
int i;
idAFEntity_VehicleSimple * focusVehicle;
focusVehicle = static_cast<idAFEntity_VehicleSimple *>( this );
focusVehicle->stoppingState = 1;
}
}
void idAFEntity_Base::Event_SetAFActive( int value )
{
if (value <= 0)
this->af.Rest();
else
this->af.Start();
}
/*
===============================================================================
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;
#ifdef _D3XP
wasThrown = false;
#endif
}
/*
================
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 );
#ifdef _D3XP
savefile->WriteBool( wasThrown );
#endif
}
/*
================
idAFEntity_Gibbable::Restore
================
*/
void idAFEntity_Gibbable::Restore( idRestoreGame *savefile ) {
bool hasCombatModel;
savefile->ReadBool( gibbed );
savefile->ReadBool( hasCombatModel );
#ifdef _D3XP
savefile->ReadBool( wasThrown );
#endif
InitSkeletonModel();
if ( hasCombatModel ) {
SetCombatModel();
LinkCombat();
}
}
/*
================
idAFEntity_Gibbable::Spawn
================
*/
void idAFEntity_Gibbable::Spawn( void ) {
InitSkeletonModel();
gibbed = false;
#ifdef _D3XP
wasThrown = false;
#endif
}
/*
================
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 );
}
}
#ifdef _D3XP
/*
=====================
idAFEntity_Gibbable::SetThrown
=====================
*/
void idAFEntity_Gibbable::SetThrown( bool isThrown ) {
if ( isThrown ) {
int i, num = af.GetPhysics()->GetNumBodies();
for ( i=0; i<num; i++ ) {
idAFBody *body;
body = af.GetPhysics()->GetBody( i );
body->SetClipMask( MASK_MONSTERSOLID );
}
}
wasThrown = isThrown;
}
/*
=====================
idAFEntity_Gibbable::Collide
=====================
*/
bool idAFEntity_Gibbable::Collide( const trace_t &collision, const idVec3 &velocity ) {
if ( !gibbed && wasThrown ) {
// Everything gibs (if possible)
if ( spawnArgs.GetBool( "gib" ) ) {
idEntity *ent;
ent = gameLocal.entities[ collision.c.entityNum ];
if ( ent->fl.takedamage ) {
ent->Damage( this, gameLocal.GetLocalPlayer(), collision.c.normal, "damage_thrown_ragdoll", 1.f, CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) );
}
idVec3 vel = velocity;
vel.NormalizeFast();
Gib( vel, "damage_gib" );
}
}
return idAFEntity_Base::Collide( collision, velocity );
}
#endif
/*
=====================
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 );
const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
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 {
#ifdef _D3XP
list[i]->GetPhysics()->SetContents( 0 );
#else
list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE );
#endif
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 * 75.0f );
}
#ifdef _D3XP
// Don't allow grabber to pick up temporary gibs
list[i]->noGrab = true;
#endif
list[i]->GetRenderEntity()->noShadow = true;
list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
list[i]->PostEventSec( &EV_Remove, 4.0f );
}
}
/*
============
idAFEntity_Gibbable::Gib
============
*/
void idAFEntity_Gibbable::Gib( const idVec3 &dir, const char *damageDefName ) {
// only gib once
if ( gibbed ) {
return;
}
#ifdef _D3XP
// Don't grab this ent after it's been gibbed (and now invisible!)
noGrab = true;
#endif
const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
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();
if ( g_bloodEffects.GetBool() ) {
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;
}
PostEventSec( &EV_Gibbed, 4.0f );
}
/*
============
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() );
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 ) {
head.GetEntity()->GetAnimator()->SetFrame( ANIMCHANNEL_ALL, anim, 0, gameLocal.time, 0 );
}
}
}
/*
================
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( void ) {
idAFAttachment *headEnt;
idStr jointName;
const char *headModel;
jointHandle_t joint;
idVec3 origin;
idMat3 axis;
headModel = spawnArgs.GetString( "def_head", "" );
if ( headModel[ 0 ] ) {
jointName = spawnArgs.GetString( "head_joint" );
joint = animator.GetJointHandle( jointName );
if ( joint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
}
headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, NULL ) );
headEnt->SetName( va( "%s_head", name.c_str() ) );
headEnt->SetBody( this, headModel, joint );
headEnt->SetCombatModel();
head = headEnt;
#ifdef _D3XP
idStr xSkin;
if ( spawnArgs.GetString( "skin_head_xray", "", xSkin ) ) {
headEnt->xraySkin = declManager->FindSkin( xSkin.c_str() );
headEnt->UpdateModel();
}
#endif
animator.GetJointTransform( joint, gameLocal.time, origin, axis );
origin = renderEntity.origin + origin * renderEntity.axis;
headEnt->SetOrigin( origin );
headEnt->SetAxis( renderEntity.axis );
headEnt->BindToJoint( this, joint, true );
}
}
/*
================
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 ) {
combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
}
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 ) );
}
//BC where player is bound to. we do this so the player viewangle doesnt rotate when the car rotates.
idDict args;
idVec3 bindOrigin;
idMat3 bindAxis;
animator.GetJointTransform( eyesJoint, gameLocal.time, bindOrigin, bindAxis );
bindOrigin = renderEntity.origin + bindOrigin * renderEntity.axis;
args.Clear();
args.SetVector( "origin", bindOrigin );
args.Set( "classname", "info_location" );
args.Set( "solid", "0" );
args.SetInt( "thirdperson", 1);
gameLocal.SpawnEntityDef( args, &this->bindPoint );
this->bindPoint->Bind(this, false);
}
/*
================
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
{
//move to spot.
player = other;
animator.GetJointTransform( eyesJoint, gameLocal.time, origin, axis );
origin = renderEntity.origin + origin * renderEntity.axis;
player->GetPhysics()->SetOrigin( origin );
//set angle.
player->SetViewAngles( renderEntity.axis.ToAngles() );
//bind.
//player->BindToBody( this, 0, true );
player->Bind(this->bindPoint, false);
//activate car.
af.GetPhysics()->SetComeToRest( false );
af.GetPhysics()->Activate();
}
}
/*
================
idAFEntity_Vehicle::GetSteerAngle
================
*/
float idAFEntity_Vehicle::GetSteerAngle( void )
{
float idealSteerAngle, angleDelta;
if (g_vehicleControlType.GetInteger() <= 0)
{
float cameraYaw = player->viewAngles.yaw;
float buggyYaw = this->renderEntity.axis.ToAngles().yaw;
cameraYaw = idMath::AngleNormalize360(cameraYaw);
buggyYaw = idMath::AngleNormalize360(buggyYaw);
if (player->usercmd.forwardmove == 0)
{
idealSteerAngle = 0;
}
else
{
float delta_yaw;
delta_yaw = buggyYaw - cameraYaw;
if (idMath::Fabs( delta_yaw ) > 180.0f )
{
if (delta_yaw > 0)
{
delta_yaw = delta_yaw - 360;
}
else
{
delta_yaw = delta_yaw + 360;
}
}
delta_yaw = idMath::ClampFloat( -STEER_MAXANGLE, STEER_MAXANGLE, delta_yaw );
if (player->usercmd.forwardmove < 0)
delta_yaw = -delta_yaw;
idealSteerAngle = delta_yaw;
}
}
else
{
//direct control.
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;
}
if (idMath::Fabs( steerAngle) <= 1.0f)
{
steerAngle = 0.0f;
}
//gameLocal.GetLocalPlayer()->DebugMessage(va("ang %f\n", steerAngle));
return steerAngle;
}
/*
===============================================================================
idAFEntity_VehicleSimple
===============================================================================
*/
const idEventDef EV_Vehicle_isLightsOff( "isLightsOff", NULL, 'd' );
CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSimple )
EVENT( EV_Vehicle_isLightsOff, idAFEntity_VehicleSimple::Event_isLightsOff )
END_CLASS
void idAFEntity_VehicleSimple::Save( idSaveGame *savefile ) const
{
//bc todo: fix this
savefile->WriteInt(stoppingState);
savefile->WriteInt(ignitionEndtime);
savefile->WriteBool(isAI);
savefile->WriteBool(attackHeld);
savefile->WriteBool(gasHeld);
savefile->WriteClipModel(wheelModel);
int i;
for (i = 0; i < 4; i++)
{
savefile->WriteFloat(wheelAngles[i]);
savefile->WriteJoint(wheelJoints[i]);
//BC todo: fix vehicle saving.
suspension[i]->Save(savefile);
}
}
void idAFEntity_VehicleSimple::Restore( idRestoreGame *savefile )
{
//bc todo: fix this
savefile->ReadInt(stoppingState);
savefile->ReadInt(ignitionEndtime);
savefile->ReadBool(isAI);
savefile->ReadBool(attackHeld);
savefile->ReadBool(gasHeld);
savefile->ReadClipModel(wheelModel);
int i;
for (i = 0; i < 4; i++)
{
savefile->ReadFloat(wheelAngles[i]);
savefile->ReadJoint(wheelJoints[i]);
suspension[i] = NULL; //BC todo: fix save/load on this vehicle.
suspension[i] = static_cast<idAFConstraint_Suspension *>( af.GetPhysics()->GetConstraint( va("suspension%d", i) ) );
//suspension[i] = static_cast<idAFConstraint_Suspension *>( suspension[i] );
suspension[i]->Restore( savefile );
}
}
/*
================
idAFEntity_VehicleSimple::idAFEntity_VehicleSimple
================
*/
idAFEntity_VehicleSimple::idAFEntity_VehicleSimple( void ) {
int i;
for ( i = 0; i < 4; i++ ) {
suspension[i] = NULL;
}
}
/*
================
idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple
================
*/
idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple( void ) {
delete wheelModel;
wheelModel = NULL;
}
/*
================
idAFEntity_VehicleSimple::Spawn
================
*/
void idAFEntity_VehicleSimple::Spawn( void ) {
static const char *wheelJointKeys[] = {
"wheelJointFrontLeft",
"wheelJointFrontRight",
"wheelJointRearLeft",
"wheelJointRearRight"
};
static idVec3 wheelPoly[4] = { idVec3( 2, 2, 0 ), idVec3( 2, -2, 0 ), idVec3( -2, -2, 0 ), idVec3( -2, 2, 0 ) };
this->stoppingState = 0;
this->ignitionEndtime = 0;
this->gasHeld = false;
this->isAI = false;
int i;
idVec3 origin;
idMat3 axis;
idTraceModel trm;
trm.SetupPolygon( wheelPoly, 4 );
trm.Translate( idVec3( 0, 0, -wheelRadius ) );
wheelModel = new idClipModel( trm );
for ( i = 0; i < 4; i++ ) {
const char *wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
if ( !wheelJointName[0] ) {
gameLocal.Error( "idAFEntity_VehicleSimple '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
}
wheelJoints[i] = animator.GetJointHandle( wheelJointName );
if ( wheelJoints[i] == INVALID_JOINT ) {
gameLocal.Error( "idAFEntity_VehicleSimple '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
}
GetAnimator()->GetJointTransform( wheelJoints[i], 0, origin, axis );
origin = renderEntity.origin + origin * renderEntity.axis;
suspension[i] = new idAFConstraint_Suspension();
suspension[i]->Setup( va( "suspension%d", i ), af.GetPhysics()->GetBody( 0 ), origin, af.GetPhysics()->GetAxis( 0 ), wheelModel );
suspension[i]->SetSuspension( g_vehicleSuspensionUp.GetFloat(),
g_vehicleSuspensionDown.GetFloat(),
g_vehicleSuspensionKCompress.GetFloat(),
g_vehicleSuspensionDamping.GetFloat(),
g_vehicleTireFriction.GetFloat() );
af.GetPhysics()->AddConstraint( suspension[i] );
}
memset( wheelAngles, 0, sizeof( wheelAngles ) );
BecomeActive( TH_THINK );
}
void idAFEntity_VehicleSimple::SpawnSuspension( void )
{
}
void idAFEntity_VehicleSimple::Event_isLightsOff( void )
{
//if engine is running then vehicle is definitely NOT hiding.
if (this->stoppingState <= 0)
{
idThread::ReturnInt( 0 );
return;
}
idThread::ReturnInt( 1 );
return;
}
/*
================
idAFEntity_VehicleSimple::Think
================
*/
void idAFEntity_VehicleSimple::Think( void ) {
int i;
float force = 0.0f, velocity = 0.0f;
idVec3 origin;
idMat3 axis;
idRotation wheelRotation, steerRotation;
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();
if (g_vehicleControlType.GetInteger() <= 0)
{
idAFBody *body1 = af.GetPhysics()->GetBody( 0 );
float curspeed = body1->GetPointVelocity( this->GetPhysics()->GetOrigin() ) * body1->GetWorldAxis()[0];
if (player->usercmd.forwardmove > 0 && curspeed < -15)
steerAngle = 0; //if accelerating while moving backwards, then just move straight.
else if (player->usercmd.forwardmove < 0 && curspeed > 15)
steerAngle = 0; //if braking while moving forward, then just move straight.
}
//bc brake lights.
if (player->usercmd.forwardmove < 0)
{
this->renderEntity.shaderParms[ 7 ] = 1.0f;
}
else
{
this->renderEntity.shaderParms[ 7 ] = 0.0f;
}
//attack.
if ((player->usercmd.buttons & BUTTON_ATTACK) && !attackHeld && this->stoppingState == 0)
{
attackHeld = true;
player->UseFrob(this, "onTurnoff");
}
if (!(player->usercmd.buttons & BUTTON_ATTACK) && attackHeld)
{
attackHeld = false;
}
if (player->usercmd.forwardmove != 0)
{
if (this->stoppingState == 0 && !this->gasHeld)
{
this->gasHeld = true;
this->StartSound( "snd_cargas" , SND_CHANNEL_BODY, 0, false, NULL );
this->StopSound(SND_CHANNEL_VOICE2, false);
}
}
else if (this->gasHeld)
{
this->gasHeld = false;
if (this->stoppingState == 0)
{
this->StartSound( "snd_caridle" , SND_CHANNEL_VOICE2, 0, false, NULL ); //start idle noise.
}
this->StopSound(SND_CHANNEL_BODY, false); //stop gas noise.
this->StartSound( "snd_cargasend" , SND_CHANNEL_BODY, 0, false, NULL ); //start idle noise.
}
}
else if (this->isAI)
{
velocity = g_vehicleVelocity.GetFloat();
velocity *= 1.1f;
force = idMath::Fabs( 127 * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
force *= 1.2f;
}
if (this->stoppingState == 1)
{
idAFBody *body1 = af.GetPhysics()->GetBody( 0 );
float curspeed = body1->GetPointVelocity( this->GetPhysics()->GetOrigin() ) * body1->GetWorldAxis()[0];
float force = idMath::Fabs( 127 * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
for ( i = 0; i < 4; i++ )
{
this->suspension[i]->EnableMotor( true );
this->suspension[i]->SetMotorVelocity( -curspeed );
this->suspension[i]->SetMotorForce( force );
}
if (idMath::Fabs( curspeed ) <= 2)
{
this->stoppingState = 2;
this->af.Rest();
}
}
else if (this->stoppingState == 2)
{
if (player)
{
if (player->usercmd.forwardmove != 0 || (player->usercmd.buttons & BUTTON_ATTACK && !this->attackHeld))
{
//turn car back on.
if (player->usercmd.buttons & BUTTON_ATTACK)
this->attackHeld = true;
this->StartSound( "snd_carignition", SND_CHANNEL_VOICE, 0, false, NULL );
this->ignitionEndtime = gameLocal.time + 700;
this->stoppingState = 3;
}
}
}
else if (this->stoppingState == 3)
{
if (ignitionEndtime <= gameLocal.time )
{
this->af.Start();
for ( i = 0; i < 4; i++ )
{
this->suspension[i]->EnableMotor( false );
}
this->af.GetPhysics()->SetComeToRest( false );
this->af.GetPhysics()->Activate();
this->stoppingState = 0;
player->UseFrob( this, "onTurnon" );
}
}
else
{
//gameLocal.GetLocalPlayer()->DebugMessage(va("vel %f %f\n", velocity, force));
// update the wheel motor force and steering
for ( i = 0; i < 2; i++ ) {
// front wheel drive
if ( velocity != 0.0f ) {
suspension[i]->EnableMotor( true );
} else {
suspension[i]->EnableMotor( false );
}
suspension[i]->SetMotorVelocity( velocity );
suspension[i]->SetMotorForce( force );
// update the wheel steering
suspension[i]->SetSteerAngle( steerAngle );
}
}
// adjust wheel velocity for better steering because there are no differentials between the wheels
if ( steerAngle < 0.0f ) {
suspension[0]->SetMotorVelocity( velocity * 0.5f );
} else if ( steerAngle > 0.0f ) {
suspension[1]->SetMotorVelocity( velocity * 0.5f );
}
// update suspension with latest cvar settings
for ( i = 0; i < 4; i++ ) {
suspension[i]->SetSuspension( g_vehicleSuspensionUp.GetFloat(),
g_vehicleSuspensionDown.GetFloat(),
g_vehicleSuspensionKCompress.GetFloat(),
g_vehicleSuspensionDamping.GetFloat(),
g_vehicleTireFriction.GetFloat() );
}
idRotation rotation;
//bc steering wheel
animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
rotation.SetVec( axis[1] );
rotation.SetAngle( steerAngle * 1.5f );
animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
// run the physics
RunPhysics();
// move and rotate the wheels visually
for ( i = 0; i < 4; i++ ) {
idAFBody *body = af.GetPhysics()->GetBody( 0 );
origin = suspension[i]->GetWheelOrigin();
velocity = body->GetPointVelocity( origin ) * body->GetWorldAxis()[0];
wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
// additional rotation about the wheel axis
wheelRotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
wheelRotation.SetVec( 0, -1, 0 );
if ( i < 2 ) {
// rotate the wheel for steering
steerRotation.SetAngle( steerAngle );
steerRotation.SetVec( 0, 0, 1 );
// set wheel rotation
animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() * steerRotation.ToMat3() );
} else {
// set wheel rotation
animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() );
}
// set wheel position for suspension
origin = ( origin - renderEntity.origin ) * renderEntity.axis.Transpose();
GetAnimator()->SetJointPos( wheelJoints[i], JOINTMOD_WORLD_OVERRIDE, origin );
}
// spawn dust particle effects
/*
if ( force != 0.0f && !( gameLocal.framenum & 7 ) )
{
int numContacts;
idAFConstraint_Contact *contacts[2];
for ( i = 0; i < 4; i++ )
{
numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
for ( int j = 0; j < numContacts; j++ )
{
gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), 0 );
}
}
}
*/
//bc wheelsmoke
if ( force != 0.0f && !( gameLocal.framenum & 3 ) && this->stoppingState == 0)
{
for ( i = 2; i < 4; i++ )
{
idVec3 wheelorigin = suspension[i]->GetWheelOrigin();
wheelorigin[2] -= this->wheelRadius * 0.8f;
gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(),
wheelorigin,
this->renderEntity.axis,
0 );
}
}
}
UpdateAnimation();
if ( thinkFlags & TH_UPDATEVISUALS )
{
Present();
LinkCombat();
}
}
/*
===============================================================================
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];
}
wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
// 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() );
}
// spawn dust particle effects
if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
int numContacts;
idAFConstraint_Contact *contacts[2];
for ( i = 0; i < 4; i++ ) {
numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
for ( int j = 0; j < numContacts; j++ ) {
gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
}
}
}
}
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;
#ifndef _D3XP
float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
#endif
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];
}
wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
// 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() );
}
// spawn dust particle effects
if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
int numContacts;
idAFConstraint_Contact *contacts[2];
for ( i = 0; i < 6; i++ ) {
numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
for ( int j = 0; j < numContacts; j++ ) {
gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3(), timeGroup /* D3XP */ );
}
}
}
}
UpdateAnimation();
if ( thinkFlags & TH_UPDATEVISUALS ) {
Present();
LinkCombat();
}
}
#ifdef _D3XP
/*
===============================================================================
idAFEntity_VehicleAutomated
===============================================================================
*/
const idEventDef EV_Vehicle_setVelocity( "setVelocity", "f" );
const idEventDef EV_Vehicle_setTorque( "setTorque", "f" );
const idEventDef EV_Vehicle_setSteeringSpeed( "setSteeringSpeed", "f" );
const idEventDef EV_Vehicle_setWaypoint( "setWaypoint", "e" );
CLASS_DECLARATION( idAFEntity_VehicleSimple, idAFEntity_VehicleAutomated )
EVENT( EV_PostSpawn, idAFEntity_VehicleAutomated::PostSpawn )
EVENT( EV_Vehicle_setVelocity, idAFEntity_VehicleAutomated::Event_SetVelocity )
EVENT( EV_Vehicle_setTorque, idAFEntity_VehicleAutomated::Event_SetTorque )
EVENT( EV_Vehicle_setSteeringSpeed, idAFEntity_VehicleAutomated::Event_SetSteeringSpeed )
EVENT( EV_Vehicle_setWaypoint, idAFEntity_VehicleAutomated::Event_SetWayPoint )
END_CLASS
void idAFEntity_VehicleAutomated::Save( idSaveGame *savefile ) const
{
savefile->WriteFloat(steeringSpeed);
savefile->WriteFloat(currentSteering);
savefile->WriteFloat(idealSteering);
savefile->WriteFloat(originHeight);
savefile->WriteObject(waypoint);
}
void idAFEntity_VehicleAutomated::Restore( idRestoreGame *savefile )
{
savefile->ReadFloat(steeringSpeed);
savefile->ReadFloat(currentSteering);
savefile->ReadFloat(idealSteering);
savefile->ReadFloat(originHeight);
savefile->ReadObject(reinterpret_cast<idClass *&>(waypoint));
}
/*
================
idAFEntity_VehicleAutomated::Spawn
================
*/
void idAFEntity_VehicleAutomated::Spawn( void ) {
//velocity = force = steerAngle = 0.f;
currentSteering = steeringSpeed = 0.f;
originHeight = 0.f;
waypoint = NULL;
this->isAI = true;
//spawnArgs.GetFloat( "velocity", "150", velocity );
//spawnArgs.GetFloat( "torque", "200000", force );
spawnArgs.GetFloat( "steeringSpeed", "1", steeringSpeed );
spawnArgs.GetFloat( "originHeight", "0", originHeight );
PostEventMS( &EV_PostSpawn, 0 );
}
/*
================
idAFEntity_VehicleAutomated::PostSpawn
================
*/
void idAFEntity_VehicleAutomated::PostSpawn( void ) {
if ( targets.Num() ) {
waypoint = targets[0].GetEntity();
}
}
/*
================
idAFEntity_VehicleAutomated::Event_SetVelocity
================
*/
void idAFEntity_VehicleAutomated::Event_SetVelocity( float _velocity ) {
//velocity = _velocity;
}
/*
================
idAFEntity_VehicleAutomated::Event_SetTorque
================
*/
void idAFEntity_VehicleAutomated::Event_SetTorque( float _torque ) {
//force = _torque;
}
/*
================
idAFEntity_VehicleAutomated::Event_SetSteeringSpeed
================
*/
void idAFEntity_VehicleAutomated::Event_SetSteeringSpeed( float _steeringSpeed ) {
steeringSpeed = _steeringSpeed;
}
/*
================
idAFEntity_VehicleAutomated::Event_SetWayPoint
================
*/
void idAFEntity_VehicleAutomated::Event_SetWayPoint( idEntity *_waypoint ) {
waypoint = _waypoint;
}
/*
================
idAFEntity_VehicleAutomated::Think
================
*/
void idAFEntity_VehicleAutomated::Think( void ) {
// If we don't have a waypoint, coast to a stop
if ( !waypoint ) {
//velocity = force = steerAngle = 0.f;
idAFEntity_VehicleSimple::Think();
return;
}
idVec3 waypoint_origin, vehicle_origin;
idVec3 travel_vector;
float distance_from_waypoint;
// Set up the vector from the vehicle origin, to the waypoint
vehicle_origin = GetPhysics()->GetOrigin();
vehicle_origin.z -= originHeight;
waypoint_origin = waypoint->GetPhysics()->GetOrigin();
travel_vector = waypoint_origin - vehicle_origin;
distance_from_waypoint = travel_vector.Length();
// Check if we've hit the waypoint (within a certain threshold)
if ( distance_from_waypoint < HIT_WAYPOINT_THRESHOLD ) {
idStr callfunc;
const function_t *func;
idThread *thread;
// Waypoints can call script functions
waypoint->spawnArgs.GetString( "call", "", callfunc );
if ( callfunc.Length() ) {
func = gameLocal.program.FindFunction( callfunc );
if ( func != NULL ) {
thread = new idThread( func );
thread->DelayedStart( 0 );
}
}
// Get next waypoint
if ( waypoint->targets.Num() ) {
waypoint = waypoint->targets[0].GetEntity();
} else {
waypoint = NULL;
}
// We are switching waypoints, adjust steering next frame
idAFEntity_VehicleSimple::Think();
return;
}
idAngles vehicle_angles, travel_angles;
// Get the angles we need to steer towards
travel_angles = travel_vector.ToAngles().Normalize360();
vehicle_angles = this->GetPhysics()->GetAxis().ToAngles().Normalize360();
float delta_yaw;
// Get the shortest steering angle towards the travel angles
delta_yaw = vehicle_angles.yaw - travel_angles.yaw;
if ( idMath::Fabs( delta_yaw ) > 180.f ) {
if ( delta_yaw > 0 ) {
delta_yaw = delta_yaw - 360;
} else {
delta_yaw = delta_yaw + 360;
}
}
// Maximum steering angle is 35 degrees
delta_yaw = idMath::ClampFloat( -STEER_MAXANGLE_AI, STEER_MAXANGLE_AI, delta_yaw );
idealSteering = delta_yaw;
// Adjust steering incrementally so it doesn't snap to the ideal angle
if ( idMath::Fabs( (idealSteering - currentSteering) ) > steeringSpeed ) {
if ( idealSteering > currentSteering ) {
currentSteering += steeringSpeed;
} else {
currentSteering -= steeringSpeed;
}
} else {
currentSteering = idealSteering;
}
// DEBUG
if ( g_vehicleDebug.GetBool() ) {
gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), vehicle_origin );
gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-4,-4,-4),idVec3(4,4,4)), waypoint_origin );
gameRenderWorld->DrawText( waypoint->name.c_str(), waypoint_origin + idVec3(0,0,16), 0.25f, colorYellow, gameLocal.GetLocalPlayer()->viewAxis );
gameRenderWorld->DebugArrow( colorWhite, vehicle_origin, waypoint_origin, 12.f );
}
// Set the final steerAngle for the vehicle
steerAngle = currentSteering;
idAFEntity_VehicleSimple::Think();
}
#endif
/*
===============================================================================
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" );
ent = static_cast<idAFEntity_Generic *>(gameLocal.SpawnEntityType( idAFEntity_Generic::Type, &args));
// 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() ) {
if ( ent->IsType( idAFEntity_Base::Type ) ) {
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() ) {
if ( ent->IsType( idAFEntity_Base::Type ) ) {
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;
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] ) );
memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) );
// 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 );
}
#ifdef _D3XP
/*
===============================================================================
idHarvestable
===============================================================================
*/
const idEventDef EV_Harvest_SpawnHarvestTrigger( "<spawnHarvestTrigger>", NULL );
CLASS_DECLARATION( idEntity, idHarvestable )
EVENT( EV_Harvest_SpawnHarvestTrigger, idHarvestable::Event_SpawnHarvestTrigger )
EVENT( EV_Touch, idHarvestable::Event_Touch )
END_CLASS
idHarvestable::idHarvestable() {
trigger = NULL;
parentEnt = NULL;
}
idHarvestable::~idHarvestable() {
if ( trigger ) {
delete trigger;
trigger = NULL;
}
}
void idHarvestable::Spawn() {
startTime = 0;
spawnArgs.GetFloat( "triggersize", "120", triggersize );
spawnArgs.GetFloat( "give_delay", "3", giveDelay);
giveDelay *= 1000;
given = false;
removeDelay = spawnArgs.GetFloat( "remove_delay") * 1000.0f;
fxFollowPlayer = spawnArgs.GetBool("fx_follow_player", "1");
fxOrient = spawnArgs.GetString("fx_orient");
}
void idHarvestable::Init(idEntity* parent) {
assert(parent);
parentEnt = parent;
GetPhysics()->SetOrigin( parent->GetPhysics()->GetOrigin() );
this->Bind(parent, true);
//Set the skin of the entity to the harvest skin
idStr skin = parent->spawnArgs.GetString("skin_harvest", "");
if(skin.Length()) {
parent->SetSkin(declManager->FindSkin(skin.c_str()));
}
idEntity* head;
if(parent->IsType(idActor::Type)) {
idActor* withHead = (idActor*)parent;
head = withHead->GetHeadEntity();
}
if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
head = withHead->head.GetEntity();
}
if(head) {
idStr headskin = parent->spawnArgs.GetString("skin_harvest_head", "");
if(headskin.Length()) {
head->SetSkin(declManager->FindSkin(headskin.c_str()));
}
}
idStr sound = parent->spawnArgs.GetString("harvest_sound");
if(sound.Length() > 0) {
parent->StartSound( sound.c_str(), SND_CHANNEL_ANY, 0, false, NULL);
}
PostEventMS( &EV_Harvest_SpawnHarvestTrigger, 0 );
}
void idHarvestable::Save( idSaveGame *savefile ) const {
savefile->WriteFloat( triggersize );
savefile->WriteClipModel( trigger );
savefile->WriteFloat( giveDelay );
savefile->WriteFloat( removeDelay );
savefile->WriteBool( given );
player.Save( savefile );
savefile->WriteInt( startTime );
savefile->WriteBool( fxFollowPlayer );
fx.Save( savefile );
savefile->WriteString( fxOrient );
parentEnt.Save(savefile);
}
void idHarvestable::Restore( idRestoreGame *savefile ) {
savefile->ReadFloat( triggersize );
savefile->ReadClipModel( trigger );
savefile->ReadFloat( giveDelay );
savefile->ReadFloat( removeDelay );
savefile->ReadBool( given );
player.Restore( savefile );
savefile->ReadInt( startTime );
savefile->ReadBool( fxFollowPlayer );
fx.Restore( savefile );
savefile->ReadString( fxOrient );
parentEnt.Restore(savefile);
}
void idHarvestable::SetParent(idEntity* parent) {
parentEnt = parent;
}
void idHarvestable::Think() {
idEntity* parent = parentEnt.GetEntity();
if(!parent) {
return;
}
//Update the orientation of the box
if(trigger && parent && !parent->GetPhysics()->IsAtRest()) {
trigger->Link( gameLocal.clip, this, 0, parent->GetPhysics()->GetOrigin(), parent->GetPhysics()->GetAxis());
}
if(startTime && gameLocal.slow.time - startTime > giveDelay && ! given) {
idPlayer *thePlayer = player.GetEntity();
thePlayer->Give(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"));
thePlayer->harvest_lock = false;
given = true;
}
if(startTime && gameLocal.slow.time - startTime > removeDelay) {
parent->PostEventMS( &EV_Remove, 0 );
PostEventMS( &EV_Remove, 0 );
}
if(fxFollowPlayer) {
idEntityFx* fxEnt = fx.GetEntity();
if(fxEnt) {
idMat3 orientAxisLocal;
if(GetFxOrientationAxis(orientAxisLocal)) {
//gameRenderWorld->DebugAxis(fxEnt->GetPhysics()->GetOrigin(), orientAxisLocal);
fxEnt->GetPhysics()->SetAxis(orientAxisLocal);
}
}
}
}
/*
================
idAFEntity_Harvest::Gib
Called when the parent object has been gibbed.
================
*/
void idHarvestable::Gib() {
//Stop any looping sound that was playing
idEntity* parent = parentEnt.GetEntity();
if(parent) {
idStr sound = parent->spawnArgs.GetString("harvest_sound");
if(sound.Length() > 0) {
parent->StopSound(SND_CHANNEL_ANY, false);
}
}
}
/*
================
idAFEntity_Harvest::BeginBurn
================
*/
void idHarvestable::BeginBurn() {
idEntity* parent = parentEnt.GetEntity();
if(!parent) {
return;
}
if(!spawnArgs.GetBool("burn")) {
return;
}
//Switch Skins if the parent would like us to.
idStr skin = parent->spawnArgs.GetString("skin_harvest_burn", "");
if(skin.Length()) {
parent->SetSkin(declManager->FindSkin(skin.c_str()));
}
parent->GetRenderEntity()->noShadow = true;
parent->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
idEntity* head;
if(parent->IsType(idActor::Type)) {
idActor* withHead = (idActor*)parent;
head = withHead->GetHeadEntity();
}
if(parent->IsType(idAFEntity_WithAttachedHead::Type)) {
idAFEntity_WithAttachedHead* withHead = (idAFEntity_WithAttachedHead*)parent;
head = withHead->head.GetEntity();
}
if(head) {
idStr headskin = parent->spawnArgs.GetString("skin_harvest_burn_head", "");
if(headskin.Length()) {
head->SetSkin(declManager->FindSkin(headskin.c_str()));
}
head->GetRenderEntity()->noShadow = true;
head->SetShaderParm( SHADERPARM_TIME_OF_DEATH, gameLocal.slow.time * 0.001f );
}
}
/*
================
idAFEntity_Harvest::BeginFX
================
*/
void idHarvestable::BeginFX() {
if(strlen(spawnArgs.GetString("fx")) <= 0) {
return;
}
idMat3* orientAxis = NULL;
idMat3 orientAxisLocal;
if(GetFxOrientationAxis(orientAxisLocal)) {
orientAxis = &orientAxisLocal;
}
fx = idEntityFx::StartFx( spawnArgs.GetString("fx"), NULL, orientAxis, this, spawnArgs.GetBool("fx_bind") );
}
/*
================
idAFEntity_Harvest::CalcTriggerBounds
================
*/
void idHarvestable::CalcTriggerBounds( float size, idBounds &bounds ) {
idEntity* parent = parentEnt.GetEntity();
if(!parent) {
return;
}
//Simple trigger bounds is the absolute bounds of the AF plus a defined size
bounds = parent->GetPhysics()->GetAbsBounds();
bounds.ExpandSelf(size);
bounds[0] -= parent->GetPhysics()->GetOrigin();
bounds[1] -= parent->GetPhysics()->GetOrigin();
}
bool idHarvestable::GetFxOrientationAxis(idMat3& mat) {
idEntity* parent = parentEnt.GetEntity();
if(!parent) {
return false;
}
idPlayer *thePlayer = player.GetEntity();
if(!fxOrient.Icmp("up")) {
//Orient up
idVec3 grav = parent->GetPhysics()->GetGravityNormal()*-1;
idVec3 left, up;
grav.OrthogonalBasis(left, up);
idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, grav.x, grav.y, grav.z);
mat = temp;
return true;
} else if(!fxOrient.Icmp("weapon")) {
//Orient the fx towards the muzzle of the weapon
jointHandle_t joint;
idVec3 joint_origin;
idMat3 joint_axis;
joint = thePlayer->weapon.GetEntity()->GetAnimator()->GetJointHandle( spawnArgs.GetString("fx_weapon_joint") );
if ( joint != INVALID_JOINT ) {
thePlayer->weapon.GetEntity()->GetJointWorldTransform( joint, gameLocal.slow.time, joint_origin, joint_axis );
} else {
joint_origin = thePlayer->GetPhysics()->GetOrigin();
}
idVec3 toPlayer = joint_origin-parent->GetPhysics()->GetOrigin();
toPlayer.NormalizeFast();
idVec3 left, up;
toPlayer.OrthogonalBasis(left, up);
idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
mat = temp;
return true;
} else if(!fxOrient.Icmp("player")) {
//Orient the fx towards the eye of the player
idVec3 eye = thePlayer->GetEyePosition();
idVec3 toPlayer = eye-parent->GetPhysics()->GetOrigin();
toPlayer.Normalize();
idVec3 left, up;
up.Set(0, 1, 0);
left = toPlayer.Cross(up);
up = left.Cross(toPlayer);
//common->Printf("%.2f %.2f %.2f - %.2f %.2f %.2f - %.2f %.2f %.2f\n", toPlayer.x, toPlayer.y, toPlayer.z, left.x, left.y, left.z, up.x, up.y, up.z );
idMat3 temp(left.x, left.y, left.z, up.x, up.y, up.z, toPlayer.x, toPlayer.y, toPlayer.z);
mat = temp;
return true;
}
//Returning false indicates that the orientation is not used;
return false;
}
/*
================
idAFEntity_Harvest::Event_SpawnHarvestTrigger
================
*/
void idHarvestable::Event_SpawnHarvestTrigger( void ) {
idBounds bounds;
idEntity* parent = parentEnt.GetEntity();
if(!parent) {
return;
}
CalcTriggerBounds( triggersize, bounds );
// create a trigger clip model
trigger = new idClipModel( idTraceModel( bounds ) );
trigger->Link( gameLocal.clip, this, 255, parent->GetPhysics()->GetOrigin(), mat3_identity);
trigger->SetContents( CONTENTS_TRIGGER );
startTime = 0;
}
/*
================
idAFEntity_Harvest::Event_Touch
================
*/
void idHarvestable::Event_Touch( idEntity *other, trace_t *trace ) {
idEntity* parent = parentEnt.GetEntity();
if(!parent) {
return;
}
if(parent->IsType(idAFEntity_Gibbable::Type)) {
idAFEntity_Gibbable* gibParent = (idAFEntity_Gibbable*)parent;
if(gibParent->IsGibbed())
return;
}
if(!startTime && other && other->IsType(idPlayer::Type)) {
idPlayer *thePlayer = static_cast<idPlayer *>(other);
if(thePlayer->harvest_lock) {
//Don't harvest if the player is in mid harvest
return;
}
player = thePlayer;
bool okToGive = true;
idStr requiredWeapons = spawnArgs.GetString("required_weapons");
if(requiredWeapons.Length() > 0) {
idStr playerWeap = thePlayer->GetCurrentWeapon();
if(playerWeap.Length() == 0 || requiredWeapons.Find(playerWeap, false) == -1) {
okToGive = false;
}
}
if(okToGive) {
if(thePlayer->CanGive(spawnArgs.GetString("give_item"), spawnArgs.GetString("give_value"))) {
startTime = gameLocal.slow.time;
//Lock the player from harvesting to prevent multiple harvests when only one is needed
thePlayer->harvest_lock = true;
idWeapon* weap = (idWeapon*)thePlayer->weapon.GetEntity();
if(weap) {
//weap->PostEventMS(&EV_Weapon_State, 0, "Charge", 8);
weap->ProcessEvent(&EV_Weapon_State, "Charge", 8);
}
BeginBurn();
BeginFX();
//Stop any looping sound that was playing
idStr sound = parent->spawnArgs.GetString("harvest_sound");
if(sound.Length() > 0) {
parent->StopSound(SND_CHANNEL_ANY, false);
}
//Make the parent object non-solid
parent->GetPhysics()->SetContents( 0 );
parent->GetPhysics()->GetClipModel()->Unlink();
//Turn of the trigger so it doesn't process twice
trigger->SetContents( 0 );
}
}
}
}
/*
===============================================================================
idAFEntity_Harvest
===============================================================================
*/
const idEventDef EV_Harvest_SpawnHarvestEntity( "<spawnHarvestEntity>", NULL );
CLASS_DECLARATION( idAFEntity_WithAttachedHead, idAFEntity_Harvest )
EVENT( EV_Harvest_SpawnHarvestEntity, idAFEntity_Harvest::Event_SpawnHarvestEntity )
END_CLASS
/*
================
idAFEntity_Harvest::idAFEntity_Harvest
================
*/
idAFEntity_Harvest::idAFEntity_Harvest() {
harvestEnt = NULL;
}
/*
================
idAFEntity_Harvest::~idAFEntity_Harvest
================
*/
idAFEntity_Harvest::~idAFEntity_Harvest() {
if ( harvestEnt.GetEntity() ) {
harvestEnt.GetEntity()->PostEventMS( &EV_Remove, 0 );
}
}
/*
================
idAFEntity_Harvest::Save
================
*/
void idAFEntity_Harvest::Save( idSaveGame *savefile ) const {
harvestEnt.Save(savefile);
}
/*
================
idAFEntity_Harvest::Restore
================
*/
void idAFEntity_Harvest::Restore( idRestoreGame *savefile ) {
harvestEnt.Restore(savefile);
//if(harvestEnt.GetEntity()) {
// harvestEnt.GetEntity()->SetParent(this);
//}
}
/*
================
idAFEntity_Harvest::Spawn
================
*/
void idAFEntity_Harvest::Spawn( void ) {
PostEventMS( &EV_Harvest_SpawnHarvestEntity, 0 );
}
/*
================
idAFEntity_Harvest::Think
================
*/
void idAFEntity_Harvest::Think( void ) {
idAFEntity_WithAttachedHead::Think();
}
void idAFEntity_Harvest::Event_SpawnHarvestEntity( void ) {
const idDict *harvestDef = gameLocal.FindEntityDefDict( spawnArgs.GetString("def_harvest_type"), false );
if ( harvestDef ) {
idEntity *temp;
gameLocal.SpawnEntityDef( *harvestDef, &temp, false );
harvestEnt = static_cast<idHarvestable *>(temp);
}
if(harvestEnt.GetEntity()) {
//Let the harvest entity set itself up
harvestEnt.GetEntity()->Init(this);
harvestEnt.GetEntity()->BecomeActive( TH_THINK );
}
}
void idAFEntity_Harvest::Gib( const idVec3 &dir, const char *damageDefName ) {
if(harvestEnt.GetEntity()) {
//Let the harvest ent know that we gibbed
harvestEnt.GetEntity()->Gib();
}
idAFEntity_WithAttachedHead::Gib(dir, damageDefName);
}
#endif