quake4-sdk/source/mpgame/Actor.cpp

3854 lines
98 KiB
C++

// RAVEN BEGIN
// bdube: note that this file is no longer merged with Doom3 updates
//
// MERGE_DATE 09/30/2004
#include "../idlib/precompiled.h"
#pragma hdrstop
#include "Game_local.h"
#if !defined(__GAME_PROJECTILE_H__)
#include "Projectile.h"
#endif
#if !defined(__GAME_VEHICLE_H__)
#include "Vehicle/Vehicle.h"
#endif
#include "ai/AI.h"
#include "ai/AI_Manager.h"
/***********************************************************************
idAnimState
***********************************************************************/
/*
=====================
idAnimState::idAnimState
=====================
*/
idAnimState::idAnimState() {
self = NULL;
animator = NULL;
idleAnim = true;
disabled = true;
channel = ANIMCHANNEL_ALL;
animBlendFrames = 0;
lastAnimBlendFrames = 0;
}
/*
=====================
idAnimState::~idAnimState
=====================
*/
idAnimState::~idAnimState() {
}
/*
=====================
idAnimState::Save
=====================
*/
void idAnimState::Save( idSaveGame *savefile ) const {
savefile->WriteBool( idleAnim );
savefile->WriteInt( animBlendFrames );
savefile->WriteInt( lastAnimBlendFrames );
savefile->WriteObject( self );
// Save the entity owner of the animator
savefile->WriteObject( animator->GetEntity() );
savefile->WriteInt( channel );
savefile->WriteBool( disabled );
// RAVEN BEGIN
// abahr:
stateThread.Save( savefile );
// RAVEN END
}
/*
=====================
idAnimState::Restore
=====================
*/
void idAnimState::Restore( idRestoreGame *savefile ) {
savefile->ReadBool( idleAnim );
savefile->ReadInt( animBlendFrames );
savefile->ReadInt( lastAnimBlendFrames );
savefile->ReadObject( reinterpret_cast<idClass *&>( self ) );
idEntity *animowner;
savefile->ReadObject( reinterpret_cast<idClass *&>( animowner ) );
if ( animowner ) {
animator = animowner->GetAnimator();
}
savefile->ReadInt( channel );
savefile->ReadBool( disabled );
// RAVEN BEGIN
// abahr:
stateThread.Restore( savefile, self );
// RAVEN END
}
/*
=====================
idAnimState::Init
=====================
*/
// RAVEN BEGIN
// bdube: converted self to entity ptr so any entity can use it
void idAnimState::Init( idEntity *owner, idAnimator *_animator, int animchannel ) {
// RAVEN BEGIN
assert( owner );
assert( _animator );
self = owner;
animator = _animator;
channel = animchannel;
stateThread.SetName ( va("%s_anim_%d", owner->GetName(), animchannel ) );
stateThread.SetOwner ( owner );
}
/*
=====================
idAnimState::Shutdown
=====================
*/
void idAnimState::Shutdown( void ) {
stateThread.Clear ( true );
}
/*
=====================
idAnimState::PostState
=====================
*/
void idAnimState::PostState ( const char* statename, int blendFrames, int delay, int flags ) {
if ( SRESULT_OK != stateThread.PostState ( statename, blendFrames, delay, flags ) ) {
gameLocal.Error ( "Could not find state function '%s' for entity '%s'", statename, self->GetName() );
}
disabled = false;
}
/*
=====================
idAnimState::SetState
=====================
*/
void idAnimState::SetState( const char *statename, int blendFrames, int flags ) {
if ( SRESULT_OK != stateThread.SetState ( statename, blendFrames, 0, flags ) ) {
gameLocal.Error ( "Could not find state function '%s' for entity '%s'", statename, self->GetName() );
}
animBlendFrames = blendFrames;
lastAnimBlendFrames = blendFrames;
disabled = false;
idleAnim = false;
}
/*
=====================
idAnimState::StopAnim
=====================
*/
void idAnimState::StopAnim( int frames ) {
animBlendFrames = 0;
animator->Clear( channel, gameLocal.time, FRAME2MS( frames ) );
}
/*
=====================
idAnimState::PlayAnim
=====================
*/
void idAnimState::PlayAnim( int anim ) {
if ( anim ) {
animator->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
}
animBlendFrames = 0;
}
/*
=====================
idAnimState::CycleAnim
=====================
*/
void idAnimState::CycleAnim( int anim ) {
if ( anim ) {
animator->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
}
animBlendFrames = 0;
}
/*
=====================
idAnimState::BecomeIdle
=====================
*/
void idAnimState::BecomeIdle( void ) {
idleAnim = true;
}
/*
=====================
idAnimState::Disabled
=====================
*/
bool idAnimState::Disabled( void ) const {
return disabled;
}
/*
=====================
idAnimState::AnimDone
=====================
*/
bool idAnimState::AnimDone( int blendFrames ) const {
int animDoneTime;
animDoneTime = animator->CurrentAnim( channel )->GetEndTime();
if ( animDoneTime < 0 ) {
// playing a cycle
return false;
} else if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) {
return true;
} else {
return false;
}
}
/*
=====================
idAnimState::IsIdle
=====================
*/
bool idAnimState::IsIdle( void ) const {
return disabled || idleAnim;
}
/*
=====================
idAnimState::GetAnimFlags
=====================
*/
animFlags_t idAnimState::GetAnimFlags( void ) const {
animFlags_t flags;
memset( &flags, 0, sizeof( flags ) );
if ( !disabled && !AnimDone( 0 ) ) {
flags = animator->GetAnimFlags( animator->CurrentAnim( channel )->AnimNum() );
}
return flags;
}
/*
=====================
idAnimState::Enable
=====================
*/
void idAnimState::Enable( int blendFrames ) {
if ( disabled ) {
disabled = false;
animBlendFrames = blendFrames;
lastAnimBlendFrames = blendFrames;
}
}
/*
=====================
idAnimState::Disable
=====================
*/
void idAnimState::Disable( void ) {
disabled = true;
idleAnim = false;
}
/*
=====================
idAnimState::UpdateState
=====================
*/
bool idAnimState::UpdateState( void ) {
if ( disabled ) {
return false;
}
stateThread.Execute ( );
return true;
}
/***********************************************************************
idActor
***********************************************************************/
const idEventDef AI_EnableEyeFocus( "enableEyeFocus" );
const idEventDef AI_DisableEyeFocus( "disableEyeFocus" );
const idEventDef AI_EnableBlink( "enableBlinking" );
const idEventDef AI_DisableBlink( "disableBlinking" );
const idEventDef EV_Footstep( "footstep" );
const idEventDef EV_FootstepLeft( "leftFoot" );
const idEventDef EV_FootstepRight( "rightFoot" );
const idEventDef EV_EnableWalkIK( "EnableWalkIK" );
const idEventDef EV_DisableWalkIK( "DisableWalkIK" );
const idEventDef EV_EnableLegIK( "EnableLegIK", "d" );
const idEventDef EV_DisableLegIK( "DisableLegIK", "d" );
const idEventDef AI_StopAnim( "stopAnim", "dd" );
const idEventDef AI_PlayAnim( "playAnim", "ds", 'd' );
const idEventDef AI_PlayCycle( "playCycle", "ds", 'd' );
const idEventDef AI_IdleAnim( "idleAnim", "ds", 'd' );
const idEventDef AI_SetSyncedAnimWeight( "setSyncedAnimWeight", "ddf" );
const idEventDef AI_SetBlendFrames( "setBlendFrames", "dd" );
const idEventDef AI_GetBlendFrames( "getBlendFrames", "d", 'd' );
const idEventDef AI_AnimDone( "animDone", "dd", 'd' );
const idEventDef AI_OverrideAnim( "overrideAnim", "d" );
const idEventDef AI_EnableAnim( "enableAnim", "dd" );
const idEventDef AI_PreventPain( "preventPain", "f" );
const idEventDef AI_DisablePain( "disablePain" );
const idEventDef AI_EnablePain( "enablePain" );
const idEventDef AI_SetAnimPrefix( "setAnimPrefix", "s" );
const idEventDef AI_HasEnemies( "hasEnemies", NULL, 'd' );
const idEventDef AI_NextEnemy( "nextEnemy", "E", 'e' );
const idEventDef AI_ClosestEnemyToPoint( "closestEnemyToPoint", "v", 'e' );
const idEventDef AI_GetHead( "getHead", NULL, 'e' );
// RAVEN BEGIN
// bdube: added
const idEventDef AI_Flashlight("flashlight", "d" );
const idEventDef AI_Teleport("teleport", "vv");
const idEventDef AI_EnterVehicle ( "enterVehicle", "e" );
const idEventDef AI_ExitVehicle ( "exitVehicle", "d" );
const idEventDef AI_PostExitVehicle ( "<exitVehicle>", "d" );
//jshepard: change animation rate
const idEventDef AI_SetAnimRate ( "setAnimRate","f");
//MCG: damage over time
const idEventDef EV_DamageOverTime ( "damageOverTime","ddEEvsfd" );
const idEventDef EV_DamageOverTimeEffect ( "damageOverTimeEffect","dds" );
// MCG: script-callable joint crawl effect
const idEventDef EV_JointCrawlEffect ( "jointCrawlEffect","sf" );
// RAVEN END
CLASS_DECLARATION( idAFEntity_Gibbable, idActor )
EVENT( AI_EnableEyeFocus, idActor::Event_EnableEyeFocus )
EVENT( AI_DisableEyeFocus, idActor::Event_DisableEyeFocus )
EVENT( AI_EnableBlink, idActor::Event_EnableBlink )
EVENT( AI_DisableBlink, idActor::Event_DisableBlink )
EVENT( EV_Footstep, idActor::Event_Footstep )
EVENT( EV_FootstepLeft, idActor::Event_Footstep )
EVENT( EV_FootstepRight, idActor::Event_Footstep )
EVENT( EV_EnableWalkIK, idActor::Event_EnableWalkIK )
EVENT( EV_DisableWalkIK, idActor::Event_DisableWalkIK )
EVENT( EV_EnableLegIK, idActor::Event_EnableLegIK )
EVENT( EV_DisableLegIK, idActor::Event_DisableLegIK )
EVENT( AI_PreventPain, idActor::Event_PreventPain )
EVENT( AI_DisablePain, idActor::Event_DisablePain )
EVENT( AI_EnablePain, idActor::Event_EnablePain )
EVENT( AI_SetAnimPrefix, idActor::Event_SetAnimPrefix )
EVENT( AI_SetSyncedAnimWeight, idActor::Event_SetSyncedAnimWeight )
EVENT( AI_SetBlendFrames, idActor::Event_SetBlendFrames )
EVENT( AI_GetBlendFrames, idActor::Event_GetBlendFrames )
EVENT( AI_OverrideAnim, idActor::Event_OverrideAnim )
EVENT( AI_EnableAnim, idActor::Event_EnableAnim )
EVENT( AI_HasEnemies, idActor::Event_HasEnemies )
EVENT( AI_NextEnemy, idActor::Event_NextEnemy )
EVENT( AI_ClosestEnemyToPoint, idActor::Event_ClosestEnemyToPoint )
EVENT( EV_StopSound, idActor::Event_StopSound )
EVENT( AI_GetHead, idActor::Event_GetHead )
// RAVEN BEGIN
// bdube: added
EVENT( AI_Flashlight, idActor::Event_Flashlight )
EVENT( AI_Teleport, idActor::Event_Teleport )
EVENT( AI_EnterVehicle, idActor::Event_EnterVehicle )
// twhitaker: Yeah... this just got confusing.
// basically, I need a delay in between the time the space bar is hit and the time the person actually get's ejected.
// this is mostly for things such as screen fades when exiting a vehicle. This was the least obtrusive way I could think of.
EVENT( AI_ExitVehicle, idActor::Event_PreExitVehicle )
EVENT( AI_PostExitVehicle, idActor::Event_ExitVehicle )
// jshepard: added
EVENT( AI_SetAnimRate, idActor::Event_SetAnimRate )
// twhitaker: added animation support (mostly for vehicle purposes)
EVENT( AI_PlayAnim, idActor::Event_PlayAnim )
// MCG: added recurring damage
EVENT( EV_DamageOverTime, idActor::Event_DamageOverTime )
EVENT( EV_DamageOverTimeEffect, idActor::Event_DamageOverTimeEffect )
// MCG: script-callable joint crawl effect
EVENT( EV_JointCrawlEffect, idActor::Event_JointCrawlEffect )
// RAVEN END
END_CLASS
CLASS_STATES_DECLARATION ( idActor )
STATE ( "Wait_Frame", idActor::State_Wait_Frame )
STATE ( "Wait_LegsAnim", idActor::State_Wait_LegsAnim )
STATE ( "Wait_TorsoAnim", idActor::State_Wait_TorsoAnim )
END_CLASS_STATES
// RAVEN END
/*
=====================
idActor::idActor
=====================
*/
idActor::idActor( void )
{
viewAxis.Identity();
use_combat_bbox = false;
head = NULL;
eyeOffset.Zero();
chestOffset.Zero();
modelOffset.Zero();
team = 0;
rank = 0;
fovDot = 0.0f;
pain_debounce_time = 0;
pain_delay = 0;
leftEyeJoint = INVALID_JOINT;
rightEyeJoint = INVALID_JOINT;
soundJoint = INVALID_JOINT;
eyeOffsetJoint = INVALID_JOINT;
chestOffsetJoint = INVALID_JOINT;
neckJoint = INVALID_JOINT;
headJoint = INVALID_JOINT;
deltaViewAngles.Zero();
painTime = 0;
inDamageEvent = false;
disablePain = true;
allowEyeFocus = false;
blink_anim = NULL;
blink_time = 0;
blink_min = 0;
blink_max = 0;
finalBoss = false;
attachments.SetGranularity( 1 );
enemyNode.SetOwner( this );
enemyList.SetOwner( this );
teamNode.SetOwner ( this );
memset( &flashlight, 0, sizeof( flashlight ) );
flashlightHandle = -1;
deathPushTime = 0;
lightningEffects = 0;
lightningNextTime = 0;
}
/*
=====================
idActor::~idActor
=====================
*/
idActor::~idActor( void ) {
// RAVEN BEGIN
// bdube: flashlights
if ( flashlightHandle != -1 ) {
gameRenderWorld->FreeLightDef( flashlightHandle );
flashlightHandle = -1;
}
// RAVEN END
int i;
idEntity *ent;
StopSound( SND_CHANNEL_ANY, false );
delete combatModel;
combatModel = NULL;
if ( head.GetEntity() ) {
head.GetEntity()->ClearBody();
head.GetEntity()->PostEventMS( &EV_Remove, 0 );
}
// remove any attached entities
for( i = 0; i < attachments.Num(); i++ ) {
ent = attachments[ i ].ent.GetEntity();
if ( ent ) {
ent->PostEventMS( &EV_Remove, 0 );
}
}
ShutdownThreads();
}
/*
=====================
idActor::Spawn
=====================
*/
void idActor::Spawn( void ) {
idEntity *ent;
idStr jointName;
float fovDegrees;
float fovDegreesClose;
animPrefix = "";
spawnArgs.GetInt( "rank", "0", rank );
spawnArgs.GetInt( "team", "0", team );
spawnArgs.GetVector( "offsetModel", "0 0 0", modelOffset );
spawnArgs.GetBool( "use_combat_bbox", "0", use_combat_bbox );
viewAxis = GetPhysics()->GetAxis();
spawnArgs.GetFloat( "fov", "90", fovDegrees );
spawnArgs.GetFloat( "fovClose", "200", fovDegreesClose );
spawnArgs.GetFloat( "fovCloseRange", "180", fovCloseRange );
SetFOV( fovDegrees, fovDegreesClose );
pain_debounce_time = 0;
pain_delay = SEC2MS( spawnArgs.GetFloat( "pain_delay" ) );
LoadAF ( );
walkIK.Init( this, IK_ANIM, modelOffset );
// the animation used to be set to the IK_ANIM at this point, but that was fixed, resulting in
// attachments not binding correctly, so we're stuck setting the IK_ANIM before attaching things.
animator.ClearAllAnims( gameLocal.time, 0 );
// RAVEN BEGIN
// jscott: new setframe stuff
frameBlend_t frameBlend = { 0, 0, 0, 1.0f, 0 };
animator.SetFrame( ANIMCHANNEL_ALL, animator.GetAnim( IK_ANIM ), frameBlend );
// RAVEN END
// spawn any attachments we might have
const idKeyValue *kv = spawnArgs.MatchPrefix( "def_attach", NULL );
while ( kv ) {
idDict args;
args.Set( "classname", kv->GetValue().c_str() );
// make items non-touchable so the player can't take them out of the character's hands
args.Set( "no_touch", "1" );
// don't let them drop to the floor
args.Set( "dropToFloor", "0" );
gameLocal.SpawnEntityDef( args, &ent );
if ( !ent ) {
gameLocal.Error( "Couldn't spawn '%s' to attach to entity '%s'", kv->GetValue().c_str(), name.c_str() );
} else {
Attach( ent );
}
kv = spawnArgs.MatchPrefix( "def_attach", kv );
}
SetupDamageGroups();
// MP sets up heads on players from UpdateModelSetup()
if( !gameLocal.isMultiplayer || !IsType( idPlayer::GetClassType() ) ) {
SetupHead ( );
}
// clear the bind anim
animator.ClearAllAnims( gameLocal.time, 0 );
idEntity *headEnt = head.GetEntity();
idAnimator *headAnimator;
if ( headEnt ) {
headAnimator = headEnt->GetAnimator();
} else {
headAnimator = &animator;
}
// set up blinking
blink_anim = headAnimator->GetAnim( "blink" );
blink_time = 0; // it's ok to blink right away
blink_min = SEC2MS( spawnArgs.GetFloat( "blink_min", "0.5" ) );
blink_max = SEC2MS( spawnArgs.GetFloat( "blink_max", "8" ) );
fl.allowAutoBlink = spawnArgs.GetBool( "allowAutoBlink", "1" );
if ( spawnArgs.GetString( "sound_bone", "", jointName ) ) {
soundJoint = animator.GetJointHandle( jointName );
if ( soundJoint == INVALID_JOINT ) {
gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), jointName.c_str() );
}
}
finalBoss = spawnArgs.GetBool( "finalBoss" );
// RAVEN BEGIN
// bdube: flashlight
flashlightJoint = animator.GetJointHandle( spawnArgs.GetString ( "joint_flashlight", "flashlight" ) );
memset( &flashlight, 0, sizeof( flashlight ) );
flashlight.suppressLightInViewID = entityNumber + 1;
flashlight.allowLightInViewID = 0;
flashlight.lightId = 1 + entityNumber;
flashlight.allowLightInViewID = 1;
idVec3 color;
spawnArgs.GetVector ( "flashlightColor", "1 1 1", color );
flashlight.pointLight = spawnArgs.GetBool( "flashlightPointLight", "1" );
flashlight.shader = declManager->FindMaterial( spawnArgs.GetString( "mtr_flashlight", "muzzleflash" ), false );
flashlight.shaderParms[ SHADERPARM_RED ] = color[0];
flashlight.shaderParms[ SHADERPARM_GREEN ] = color[1];
flashlight.shaderParms[ SHADERPARM_BLUE ] = color[2];
flashlight.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
// RAVEN BEGIN
// dluetscher: added a default detail level to each render light
flashlight.detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
// RAVEN END
flashlight.lightRadius[0] = flashlight.lightRadius[1] =
flashlight.lightRadius[2] = spawnArgs.GetFloat ( "flashlightRadius" );
if ( !flashlight.pointLight ) {
flashlight.target = spawnArgs.GetVector( "flashlightTarget" );
flashlight.up = spawnArgs.GetVector( "flashlightUp" );
flashlight.right = spawnArgs.GetVector( "flashlightRight" );
flashlight.end = spawnArgs.GetVector( "flashlightTarget" );
}
spawnArgs.GetVector ( "flashlightOffset", "0 0 0", flashlightOffset );
if ( spawnArgs.GetString( "flashlight_flaresurf", NULL ) ) {
HideSurface( spawnArgs.GetString( "flashlight_flaresurf", NULL ) );
}
// RAVEN END
stateThread.SetName ( GetName() );
stateThread.SetOwner ( this );
// RAVEN BEGIN
// cdr: Obstacle Avoidance
fl.isAIObstacle = true;
// RAVEN END
FinishSetup();
}
/*
================
idActor::FinishSetup
================
*/
void idActor::FinishSetup( void ) {
if ( spawnArgs.GetBool ( "flashlight", "0" ) ) {
FlashlightUpdate ( true );
}
SetupBody();
}
/*
================
idActor::SetupHead
================
*/
void idActor::SetupHead( const char* headDefName, idVec3 headOffset ) {
idAFAttachment *headEnt;
idStr jointName;
jointHandle_t joint;
const idKeyValue *sndKV;
if ( gameLocal.isClient && head.GetEntity() == NULL ) {
return;
}
// If we don't pass in a specific head model, try looking it up
if( !headDefName[ 0 ] ) {
headDefName = spawnArgs.GetString( "def_head", "" );
// jshepard: allow for heads to override persona defs
headDefName = spawnArgs.GetString( "override_head", headDefName );
}
if ( headDefName[ 0 ] ) {
// free the old head if we want a new one
if( gameLocal.isServer ) {
if( head && idStr::Icmp( head->spawnArgs.GetString( "classname" ), headDefName ) ) {
head->SetName( va( "%s_oldhead", name.c_str() ) );
head->PostEventMS( &EV_Remove, 0 );
head = NULL;
} else if( head ) {
// the current head is OK
return;
}
}
jointName = spawnArgs.GetString( "joint_head" );
joint = animator.GetJointHandle( jointName );
if ( joint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'joint_head' on '%s'", jointName.c_str(), name.c_str() );
}
// copy any sounds in case we have frame commands on the head
idDict args;
sndKV = spawnArgs.MatchPrefix( "snd_", NULL );
while( sndKV ) {
args.Set( sndKV->GetKey(), sndKV->GetValue() );
sndKV = spawnArgs.MatchPrefix( "snd_", sndKV );
}
if ( !gameLocal.isClient ) {
args.Set( "classname", headDefName );
if( !gameLocal.SpawnEntityDef( args, ( idEntity ** )&headEnt ) ) {
gameLocal.Warning( "idActor::SetupHead() - Unknown head model '%s'\n", headDefName );
return;
}
headEnt->spawnArgs.Set( "classname", headDefName );
headEnt->SetName( va( "%s_head", name.c_str() ) );
headEnt->SetBody ( this, headEnt->spawnArgs.GetString ( "model" ), joint );
head = headEnt;
} else {
// we got our spawnid from the server
headEnt = head.GetEntity();
headEnt->SetBody ( this, headEnt->spawnArgs.GetString ( "model" ), joint );
headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber + 1;
}
headEnt->BindToJoint( this, joint, true );
headEnt->GetPhysics()->SetOrigin( vec3_origin + headOffset );
headEnt->GetPhysics()->SetAxis( mat3_identity );
} else if ( head ) {
head->PostEventMS( &EV_Remove, 0 );
head = NULL;
}
if ( head ) {
int i;
// set the damage joint to be part of the head damage group
for( i = 0; i < damageGroups.Num(); i++ ) {
if ( damageGroups[ i ] == "head" ) {
head->SetDamageJoint ( static_cast<jointHandle_t>( i ) );
break;
}
}
head->InitCopyJoints ( );
head->SetInstance( instance );
}
}
/*
================
idActor::Restart
================
*/
void idActor::Restart( void ) {
assert( !head.GetEntity() );
// MP sets up heads from UpdateModelSetup()
if( !gameLocal.isMultiplayer ) {
SetupHead();
}
FinishSetup();
}
/*
================
idActor::Save
archive object for savegame file
================
*/
void idActor::Save( idSaveGame *savefile ) const {
idActor *ent;
int i;
savefile->WriteInt( team );
// cnicholson: This line was already commented out, so we aint changing it.
// No need to write/read teamNode
// idLinkList<idActor> teamNode;
savefile->WriteInt( rank );
savefile->WriteMat3( viewAxis );
// twhitaker: this confuses me... should we be writing out enemyList.Num() or enemyList.Next()->enemyNode->Num(). I'm not sure what these variables represent.
// cnicholson: bdube said to do it how id does it, so here goes:
savefile->WriteInt( enemyList.Num() );
for ( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
savefile->WriteObject( ent );
}
savefile->WriteInt( lightningNextTime );// cnicholson: Added unwritten var
savefile->WriteInt( lightningEffects ); // cnicholson: Added unwritten var
savefile->WriteFloat( fovDot );
savefile->WriteFloat( fovCloseDot );
savefile->WriteFloat( fovCloseRange );
savefile->WriteVec3( eyeOffset );
savefile->WriteVec3( chestOffset );
savefile->WriteVec3( modelOffset );
savefile->WriteAngles( deltaViewAngles );
savefile->WriteInt( pain_debounce_time );
savefile->WriteInt( pain_delay );
savefile->WriteInt( damageGroups.Num() );
for( i = 0; i < damageGroups.Num(); i++ ) {
savefile->WriteString( damageGroups[ i ] );
}
savefile->WriteInt( damageScale.Num() );
for( i = 0; i < damageScale.Num(); i++ ) {
savefile->WriteFloat( damageScale[ i ] );
}
//MCG
savefile->WriteBool( inDamageEvent );
savefile->WriteBool( use_combat_bbox );
savefile->WriteJoint( leftEyeJoint );
savefile->WriteJoint( rightEyeJoint );
savefile->WriteJoint( soundJoint );
savefile->WriteJoint( eyeOffsetJoint );
savefile->WriteJoint( chestOffsetJoint );
savefile->WriteJoint( neckJoint );
savefile->WriteJoint( headJoint );
walkIK.Save( savefile );
savefile->WriteString( animPrefix );
savefile->WriteString( painType );
savefile->WriteString( painAnim );
savefile->WriteInt( blink_anim );
savefile->WriteInt( blink_time );
savefile->WriteInt( blink_min );
savefile->WriteInt( blink_max );
headAnim.Save( savefile );
torsoAnim.Save( savefile );
legsAnim.Save( savefile );
stateThread.Save( savefile );
// idEntityPtr<idAFAttachment> head;
head.Save( savefile ); // cnicholson: Added unwritten var
savefile->WriteBool( disablePain );
savefile->WriteBool( allowEyeFocus );
savefile->WriteBool( finalBoss );
savefile->WriteInt( painTime );
savefile->WriteInt( attachments.Num() );
for ( i = 0; i < attachments.Num(); i++ ) {
attachments[i].ent.Save( savefile );
savefile->WriteInt( attachments[i].channel );
}
vehicleController.Save ( savefile );
// These aren't saved in the same order as they're declared, due to the dependency (I didn't see the need to change the order of declaration)
savefile->WriteInt ( flashlightHandle );
savefile->WriteJoint ( flashlightJoint );
savefile->WriteVec3 ( flashlightOffset );
savefile->WriteRenderLight ( flashlight );
savefile->WriteInt( deathPushTime );
savefile->WriteVec3( deathPushForce );
savefile->WriteJoint( deathPushJoint );
}
/*
================
idActor::Restore
unarchives object from save game file
================
*/
void idActor::Restore( idRestoreGame *savefile ) {
int i, num;
idActor *ent;
savefile->ReadInt( team );
// cnicholson: This line was already commented out, so we aint changing it.
// No need to write/read teamNode
// idLinkList<idActor> teamNode;
savefile->ReadInt( rank );
savefile->ReadMat3( viewAxis );
savefile->ReadInt( num );
for ( i = 0; i < num; i++ ) {
savefile->ReadObject( reinterpret_cast<idClass *&>( ent ) );
assert( ent );
if ( ent ) {
ent->enemyNode.AddToEnd( enemyList );
}
}
savefile->ReadInt( lightningEffects );
savefile->ReadInt( lightningNextTime );
savefile->ReadFloat( fovDot );
savefile->ReadFloat( fovCloseDot );
savefile->ReadFloat( fovCloseRange );
savefile->ReadVec3( eyeOffset );
savefile->ReadVec3( chestOffset );
savefile->ReadVec3( modelOffset );
savefile->ReadAngles( deltaViewAngles );
savefile->ReadInt( pain_debounce_time );
savefile->ReadInt( pain_delay );
savefile->ReadInt( num );
damageGroups.SetGranularity( 1 );
damageGroups.SetNum( num );
for( i = 0; i < num; i++ ) {
savefile->ReadString( damageGroups[ i ] );
}
savefile->ReadInt( num );
damageScale.SetNum( num );
for( i = 0; i < num; i++ ) {
savefile->ReadFloat( damageScale[ i ] );
}
//MCG
savefile->ReadBool( inDamageEvent );
savefile->ReadBool( use_combat_bbox );
savefile->ReadJoint( leftEyeJoint );
savefile->ReadJoint( rightEyeJoint );
savefile->ReadJoint( soundJoint );
savefile->ReadJoint( eyeOffsetJoint );
savefile->ReadJoint( chestOffsetJoint );
savefile->ReadJoint( neckJoint );
savefile->ReadJoint( headJoint );
walkIK.Restore( savefile );
savefile->ReadString( animPrefix );
savefile->ReadString( painType );
savefile->ReadString( painAnim );
savefile->ReadInt( blink_anim );
savefile->ReadInt( blink_time );
savefile->ReadInt( blink_min );
savefile->ReadInt( blink_max );
headAnim.Restore( savefile );
torsoAnim.Restore( savefile );
legsAnim.Restore( savefile );
stateThread.Restore( savefile, this );
// cnicholson: Restore unread var
// idEntityPtr<idAFAttachment> head;
head.Restore(savefile);
savefile->ReadBool( disablePain );
savefile->ReadBool( allowEyeFocus );
savefile->ReadBool( finalBoss );
savefile->ReadInt( painTime );
savefile->ReadInt( num );
for ( i = 0; i < num; i++ ) {
idAttachInfo &attach = attachments.Alloc();
attach.ent.Restore( savefile );
savefile->ReadInt( attach.channel );
}
// RAVEN BEGIN
// bdube: added
vehicleController.Restore ( savefile );
savefile->ReadInt ( flashlightHandle );
savefile->ReadJoint ( flashlightJoint );
savefile->ReadVec3 ( flashlightOffset );
savefile->ReadRenderLight ( flashlight );
if ( flashlightHandle != -1 ) {
flashlightHandle = gameRenderWorld->AddLightDef( &flashlight );
}
savefile->ReadInt( deathPushTime );
savefile->ReadVec3( deathPushForce );
savefile->ReadJoint( deathPushJoint );
// mekberg: update this
FlashlightUpdate( );
// RAVEN END
}
/*
================
idActor::Hide
================
*/
void idActor::Hide( void ) {
idEntity *ent;
idEntity *next;
idAFEntity_Base::Hide();
if ( head.GetEntity() ) {
head.GetEntity()->Hide();
}
for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
next = ent->GetNextTeamEntity();
if ( ent->GetBindMaster() == this ) {
ent->Hide();
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( ent->IsType( idLight::GetClassType() ) ) {
// RAVEN END
static_cast<idLight *>( ent )->Off();
}
}
}
UnlinkCombat();
}
/*
================
idActor::Show
================
*/
void idActor::Show( void ) {
idEntity *ent;
idEntity *next;
idAFEntity_Base::Show();
if ( head.GetEntity() ) {
head.GetEntity()->Show();
}
for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
next = ent->GetNextTeamEntity();
if ( ent->GetBindMaster() == this ) {
ent->Show();
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( ent->IsType( idLight::GetClassType() ) ) {
// RAVEN END
static_cast<idLight *>( ent )->On();
}
}
}
LinkCombat();
}
/*
==============
idActor::GetDefaultSurfaceType
==============
*/
int idActor::GetDefaultSurfaceType( void ) const {
return SURFTYPE_FLESH;
}
/*
================
idActor::ProjectOverlay
================
*/
void idActor::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
idEntity *ent;
idEntity *next;
idEntity::ProjectOverlay( origin, dir, size, material );
for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
next = ent->GetNextTeamEntity();
if ( ent->GetBindMaster() == this ) {
if ( ent->fl.takedamage && ent->spawnArgs.GetBool( "bleed" ) ) {
ent->ProjectOverlay( origin, dir, size, material );
}
}
}
}
/*
================
idActor::LoadAF
================
*/
bool idActor::LoadAF( const char* keyname, bool purgeAF /* = false */ ) {
idStr fileName;
if ( !keyname || !*keyname ) {
keyname = "ragdoll";
}
if ( !spawnArgs.GetString( keyname, "*unknown*", fileName ) || !fileName.Length() ) {
return false;
}
af.SetAnimator( GetAnimator() );
return af.Load( this, fileName, purgeAF );
}
/*
=====================
idActor::SetupBody
=====================
*/
void idActor::SetupBody( void ) {
const char* jointname;
idAnimator* headAnimator;
float height;
animator.ClearAllAnims( gameLocal.time, 0 );
animator.ClearAllJoints();
// Cache the head entity pointer and determine which animator to use
idAFAttachment *headEnt = head.GetEntity();
if ( headEnt ) {
headAnimator = headEnt->GetAnimator();
} else {
headAnimator = GetAnimator( );
}
// Get left eye joint
if ( !headEnt || !headEnt->spawnArgs.GetString ( "joint_leftEye", "", &jointname ) ) {
jointname = spawnArgs.GetString( "joint_leftEye" );
}
leftEyeJoint = headAnimator->GetJointHandle( jointname );
// Get right eye joint
if ( !headEnt || !headEnt->spawnArgs.GetString ( "joint_rightEye", "", &jointname ) ) {
jointname = spawnArgs.GetString( "joint_rightEye" );
}
rightEyeJoint = headAnimator->GetJointHandle( jointname );
// If head height is specified, just use that
if ( spawnArgs.GetFloat( "eye_height", "0", height ) ) {
SetEyeHeight( height );
} else {
// See if there is an eye offset joint specified, if not just use the left eye joint
if ( !headEnt || !headEnt->spawnArgs.GetString( "joint_eyeOffset", "", &jointname ) ) {
jointname = spawnArgs.GetString( "joint_eyeOffset" );
}
// Get the eye offset joint
eyeOffsetJoint = headAnimator->GetJointHandle( jointname );
}
// If eye height is specified, just use that
if ( spawnArgs.GetFloat( "chest_height", "0", height ) ) {
SetChestHeight( height );
} else {
// See if there is an eye offset joint specified, if not just use the left eye joint
spawnArgs.GetString( "joint_chestOffset", "", &jointname );
// Get the chest offset joint
chestOffsetJoint = animator.GetJointHandle( jointname );
}
// Get the neck joint
spawnArgs.GetString( "joint_look_neck", "", &jointname );
neckJoint = animator.GetJointHandle( jointname );
// Get the head joint
spawnArgs.GetString( "joint_look_head", "", &jointname );
headJoint = animator.GetJointHandle( jointname );
headAnim.Init( this, &animator, ANIMCHANNEL_HEAD );
torsoAnim.Init( this, &animator, ANIMCHANNEL_TORSO );
legsAnim.Init( this, &animator, ANIMCHANNEL_LEGS );
}
/*
=====================
idActor::CheckBlink
=====================
*/
void idActor::CheckBlink( void ) {
// check if it's time to blink
if ( !blink_anim || ( health <= 0 ) || ( blink_time > gameLocal.time ) || !fl.allowAutoBlink ) {
return;
}
idAnimator *animator = head.GetEntity() ? head->GetAnimator() : &this->animator;
animator->PlayAnim( ANIMCHANNEL_EYELIDS, blink_anim, gameLocal.time, 1 );
// set the next blink time
blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
}
/*
================
idActor::GetPhysicsToVisualTransform
================
*/
bool idActor::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
if ( af.IsActive() ) {
af.GetPhysicsToVisualTransform( origin, axis );
return true;
}
// RAVEN BEGIN
// bdube: position player in seat (nmckenzie: copy and paste from the player version of this call)
if ( vehicleController.IsDriving ( ) ) {
vehicleController.GetDriverPosition ( origin, axis );
return true;
}
// RAVEN END
origin = modelOffset;
axis = viewAxis;
return true;
}
/*
================
idActor::GetPhysicsToSoundTransform
================
*/
bool idActor::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
if ( soundJoint != INVALID_JOINT ) {
animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
origin += modelOffset;
axis = viewAxis;
} else {
origin = GetPhysics()->GetGravityNormal() * -eyeOffset.z;
axis.Identity();
}
return true;
}
/***********************************************************************
script state management
***********************************************************************/
/*
================
idActor::ShutdownThreads
================
*/
void idActor::ShutdownThreads( void ) {
headAnim.Shutdown();
torsoAnim.Shutdown();
legsAnim.Shutdown();
}
/*
=====================
idActor::OnStateThreadClear
=====================
*/
void idActor::OnStateThreadClear( const char *statename, int flags ) {
}
/*
=====================
idActor::SetState
=====================
*/
void idActor::SetState( const char *statename, int flags ) {
OnStateThreadClear( statename, flags );
stateThread.SetState ( statename, 0, 0, flags );
}
/*
=====================
idActor::PostState
=====================
*/
void idActor::PostState ( const char* statename, int delay, int flags ) {
if ( SRESULT_OK != stateThread.PostState ( statename, 0, delay, flags ) ) {
gameLocal.Error ( "unknown state '%s' on entity '%s'", statename, GetName() );
}
}
/*
=====================
idActor::InterruptState
=====================
*/
void idActor::InterruptState ( const char* statename, int delay, int flags ) {
if ( SRESULT_OK != stateThread.InterruptState ( statename, 0, delay, flags ) ) {
gameLocal.Error ( "unknown state '%s' on entity '%s'", statename, GetName() );
}
}
/*
=====================
idActor::UpdateState
=====================
*/
void idActor::UpdateState ( void ) {
stateThread.Execute ( );
}
/***********************************************************************
vision
***********************************************************************/
/*
=====================
idActor::setFov
=====================
*/
void idActor::SetFOV( float fov, float fovClose ) {
fovDot = idMath::Cos( DEG2RAD( fov * 0.5f ) );
fovCloseDot = idMath::Cos( DEG2RAD( fovClose * 0.5f ) );
}
/*
=====================
idActor::SetEyeHeight
=====================
*/
void idActor::SetEyeHeight( float height ) {
eyeOffset = GetPhysics()->GetGravityNormal() * -height;
}
/*
=====================
idActor::EyeHeight
=====================
*/
float idActor::EyeHeight( void ) const {
return eyeOffset.z;
}
/*
=====================
idActor::SetChestHeight
=====================
*/
void idActor::SetChestHeight( float height ) {
chestOffset = GetPhysics()->GetGravityNormal() * -height;
}
/*
=====================
idActor::GetEyePosition
=====================
*/
idVec3 idActor::GetEyePosition( void ) const {
return GetPhysics()->GetOrigin() + eyeOffset * viewAxis;
}
/*
=====================
idActor::GetChestPosition
=====================
*/
idVec3 idActor::GetChestPosition( void ) const {
return GetPhysics()->GetOrigin() + chestOffset * viewAxis;
}
// RAVEN BEGIN
// bdube: flashlights
/*
=====================
idActor::GetGroundEntity
=====================
*/
idEntity* idActor::GetGroundEntity( void ) const {
return static_cast<idPhysics_Actor*>(GetPhysics())->GetGroundEntity();
}
/*
=====================
idActor::Present
=====================
*/
void idActor::Present( void ) {
idAFEntity_Gibbable::Present();
FlashlightUpdate();
}
/*
=====================
idActor::Event_Teleport
=====================
*/
void idActor::Event_Teleport( idVec3 &newPos, idVec3 &newAngles ) {
Teleport ( newPos, newAngles.ToAngles(), NULL );
}
/*
=====================
idActor::Event_EnterVehicle
=====================
*/
void idActor::Event_EnterVehicle ( idEntity* vehicle ) {
if ( IsInVehicle ( ) ) {
return;
}
EnterVehicle ( vehicle );
}
/*
=====================
idActor::Event_ExitVehicle
=====================
*/
void idActor::Event_ExitVehicle ( bool force ) {
if ( !IsInVehicle ( ) ) {
return;
}
ExitVehicle ( force );
}
/*
=====================
idActor::Event_PreExitVehicle
=====================
*/
void idActor::Event_PreExitVehicle ( bool force ) {
if ( !IsInVehicle ( ) ) {
return;
}
// call the script func regardless of the fact that we may not be getting out just yet.
// this allows things in the script to happen before ejection actually occurs (such as screen fades).
vehicleController.GetVehicle()->OnExit();
// this is done because having an exit delay when the player is dead was causing bustedness if you died in the walker
// specifically the restart menu would not appear if you were still in the walker
if ( health > 0 ) {
PostEventMS( &AI_PostExitVehicle, vehicleController.GetVehicle()->spawnArgs.GetInt( "exit_vehicle_delay" ), force );
}
else {
ExitVehicle(true);
}
}
/*
=====================
idActor::Event_Flashlight
=====================
*/
void idActor::Event_Flashlight( bool on ) {
if ( on ) {
FlashlightUpdate(true);
} else {
if ( flashlightHandle != -1 ) {
gameRenderWorld->FreeLightDef ( flashlightHandle );
flashlightHandle = -1;
}
if ( spawnArgs.GetString( "flashlight_flaresurf", NULL ) ) {
HideSurface( spawnArgs.GetString( "flashlight_flaresurf", NULL ) );
}
if ( spawnArgs.GetString( "fx_flashlight", NULL ) ) {
StopEffect( "fx_flashlight", true );
}
}
}
/*
=====================
idActor::FlashlightUpdate
=====================
*/
void idActor::FlashlightUpdate ( bool forceOn ) {
// Dont do anything if flashlight is off and its not being forced on
if ( !forceOn && flashlightHandle == -1 ) {
return;
}
if ( !flashlight.lightRadius[0] || flashlightJoint == INVALID_JOINT ) {
return;
}
if ( forceOn && flashlightHandle == -1 ) {
//first time turning it on
if ( spawnArgs.GetString( "flashlight_flaresurf", NULL ) ) {
ShowSurface( spawnArgs.GetString( "flashlight_flaresurf", NULL ) );
}
if ( spawnArgs.GetString( "fx_flashlight", NULL ) ) {
PlayEffect( "fx_flashlight", flashlightJoint, true );
}
}
// the flash has an explicit joint for locating it
GetJointWorldTransform ( flashlightJoint, gameLocal.time, flashlight.origin, flashlight.axis );
flashlight.origin += flashlightOffset * flashlight.axis;
if ( flashlightHandle != -1 ) {
gameRenderWorld->UpdateLightDef( flashlightHandle, &flashlight );
} else {
flashlightHandle = gameRenderWorld->AddLightDef( &flashlight );
}
}
// RAVEN END
/*
=====================
idActor::GetViewPos
=====================
*/
void idActor::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
origin = GetEyePosition();
axis = viewAxis;
}
/*
=====================
idActor::CheckFOV
=====================
*/
bool idActor::CheckFOV( const idVec3 &pos, float ang ) const {
float testAng = ( ang != -1.0f ) ? idMath::Cos( DEG2RAD( ang * 0.5f ) ) : fovDot;
if ( testAng == 1.0f ) {
return true;
}
if(!GetPhysics()) {
return false;
}
float dot;
float dist;
idVec3 delta;
delta = pos - GetEyePosition();
dist = delta.LengthFast();
//NOTE!!!
//This logic is BACKWARDS, but it's too late in the project
//for me to feel comfortable fixing this. It SHOULD be:
//if ( ang == -1.0f )
// - MCG
if ( ang != -1.0f )
{//not overriding dot test value
if (dist<fovCloseRange) {
testAng = fovCloseDot; // allow a wider FOV if close enough
}
}
// get our gravity normal
const idVec3 &gravityDir = GetPhysics()->GetGravityNormal();
// infinite vertical vision, so project it onto our orientation plane
delta -= gravityDir * ( gravityDir * delta );
delta.Normalize();
dot = viewAxis[ 0 ] * delta;
return ( dot >= testAng );
}
/*
=====================
idActor::HasFOV
=====================
*/
bool idActor::HasFOV( idEntity *ent )
{
// Fixme: Make this do something, anything.
return true;
}
// RAVEN END
/*
=====================
idActor::CanSee
=====================
*/
bool idActor::CanSee( const idEntity *ent, bool useFov ) const {
return CanSeeFrom ( GetEyePosition ( ), ent, useFov );
}
/*
=====================
idActor::CanSeeFrom
=====================
*/
bool idActor::CanSeeFrom ( const idVec3& from, const idEntity *ent, bool useFov ) const {
idVec3 toPos;
if ( !ent || ent->IsHidden() ) {
return false;
}
if ( ent->IsType( idActor::Type ) ) {
toPos = ((idActor*)ent)->GetEyePosition();
} else {
toPos = ent->GetPhysics()->GetAbsBounds().GetCenter ( );
}
return CanSeeFrom ( from, toPos, useFov );
}
bool idActor::CanSeeFrom ( const idVec3& from, const idVec3& toPos, bool useFov ) const {
trace_t tr;
if ( useFov && !CheckFOV( toPos ) ) {
return false;
}
if ( g_perfTest_aiNoVisTrace.GetBool() ) {
return true;
}
gameLocal.TracePoint( this, tr, from, toPos, MASK_OPAQUE, this );
if ( tr.fraction >= 1.0f ) { // || ( gameLocal.GetTraceEntity( tr ) == ent ) ) {
return true;
}
return false;
}
/*
=====================
idActor::GetRenderView
=====================
*/
renderView_t *idActor::GetRenderView() {
renderView_t *rv = idEntity::GetRenderView();
rv->viewaxis = viewAxis;
rv->vieworg = GetEyePosition();
return rv;
}
/***********************************************************************
Model/Ragdoll
***********************************************************************/
/*
================
idActor::SetCombatModel
================
*/
void idActor::SetCombatModel( void ) {
idAFAttachment *headEnt;
// RAVEN BEGIN
// bdube: set the combat model reguardless
if ( 1 ) { // !use_combat_bbox ) {
// RAVEN END
if ( combatModel ) {
combatModel->Unlink();
combatModel->LoadModel( modelDefHandle );
} else {
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
combatModel = new idClipModel( modelDefHandle );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
}
headEnt = head.GetEntity();
if ( headEnt ) {
headEnt->SetCombatModel();
}
}
}
/*
================
idActor::GetCombatModel
================
*/
idClipModel *idActor::GetCombatModel( void ) const {
return combatModel;
}
/*
================
idActor::LinkCombat
================
*/
void idActor::LinkCombat( void ) {
idAFAttachment *headEnt;
if ( fl.hidden || use_combat_bbox ) {
return;
}
if ( combatModel ) {
// RAVEN BEGIN
// ddynerman: multiple clip worlds
combatModel->Link( this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
// RAVEN END
}
headEnt = head.GetEntity();
if ( headEnt ) {
headEnt->LinkCombat();
}
}
/*
================
idActor::UnlinkCombat
================
*/
void idActor::UnlinkCombat( void ) {
idAFAttachment *headEnt;
if ( combatModel ) {
combatModel->Unlink();
}
headEnt = head.GetEntity();
if ( headEnt ) {
headEnt->UnlinkCombat();
}
}
/*
================
idActor::StartRagdoll
================
*/
bool idActor::StartRagdoll( void ) {
float slomoStart, slomoEnd;
float jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd;
float contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd;
// if no AF loaded
if ( !af.IsLoaded() ) {
return false;
}
// if the AF is already active
if ( af.IsActive() ) {
return true;
}
// Raise the origin up 5 units to help ensure the ragdoll doesnt start in the ground
GetPhysics()->SetOrigin( GetPhysics()->GetOrigin() + GetPhysics()->GetGravityNormal() * -5.0f );
UpdateModelTransform();
// disable the monster bounding box
GetPhysics()->DisableClip();
af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
slomoStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoStart", "-1.6" );
slomoEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoEnd", "0.8" );
// do the first part of the death in slow motion
af.GetPhysics()->SetTimeScaleRamp( slomoStart, slomoEnd );
jointFrictionDent = spawnArgs.GetFloat( "ragdoll_jointFrictionDent", "0.1" );
jointFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionStart", "0.2" );
jointFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionEnd", "1.2" );
// set joint friction dent
af.GetPhysics()->SetJointFrictionDent( jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd );
contactFrictionDent = spawnArgs.GetFloat( "ragdoll_contactFrictionDent", "0.1" );
contactFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionStart", "1.0" );
contactFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionEnd", "2.0" );
// set contact friction dent
af.GetPhysics()->SetContactFrictionDent( contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd );
// drop any items the actor is holding
idList<idEntity *> list;
idMoveableItem::DropItems( this, "death", &list );
for ( int i = 0; i < list.Num(); i++ ) {
if ( list[i] && list[i]->GetPhysics() )
{
idVec3 velocity;
float pitchDir = gameLocal.random.CRandomFloat()>0.0f?1.0f:-1.0f;
float yawDir = gameLocal.random.CRandomFloat()>0.0f?1.0f:-1.0f;
float rollDir = gameLocal.random.CRandomFloat()>0.0f?1.0f:-1.0f;
velocity.Set( pitchDir*((gameLocal.random.RandomFloat() * 200.0f) + 50.0f),
yawDir*((gameLocal.random.RandomFloat() * 200.0f) + 50.0f),
(gameLocal.random.RandomFloat() * 300.0f) + 100.0f );
list[i]->GetPhysics()->SetAngularVelocity( idVec3( pitchDir*((gameLocal.random.RandomFloat() * 6.0f) + 2.0f),
yawDir*((gameLocal.random.RandomFloat() * 6.0f) + 2.0f),
rollDir*((gameLocal.random.RandomFloat() * 10.0f) + 3.0f)));
if ( gibbed ) {
//only throw them if we end up gibbed?
list[i]->GetPhysics()->SetLinearVelocity( velocity );
}
}
}
// drop any articulated figures the actor is holding
idAFEntity_Base::DropAFs( this, "death", NULL );
RemoveAttachments();
// RAVEN BEGIN
// bdube: evaluate one ragdoll frame
RunPhysics();
// RAVEN END
return true;
}
/*
================
idActor::StopRagdoll
================
*/
void idActor::StopRagdoll( void ) {
if ( af.IsActive() ) {
af.Stop();
}
}
/*
================
idActor::UpdateAnimationControllers
================
*/
bool idActor::UpdateAnimationControllers( void ) {
if ( af.IsActive() ) {
return idAFEntity_Gibbable::UpdateAnimationControllers();
} else {
animator.ClearAFPose();
}
if ( walkIK.IsInitialized() ) {
walkIK.Evaluate();
return true;
}
idMat3 axis;
idAnimatedEntity* headEnt = head.GetEntity( );
if ( !headEnt ) {
headEnt = this;
}
// Dynamically update the eye offset if a joint was specified
if ( eyeOffsetJoint != INVALID_JOINT ) {
headEnt->GetJointWorldTransform( eyeOffsetJoint, gameLocal.time, eyeOffset, axis );
eyeOffset = (eyeOffset - GetPhysics()->GetOrigin()) * viewAxis.Transpose( );
}
if ( DebugFilter( ai_debugMove ) ) { // RED = Eye Pos & orientation
gameRenderWorld->DebugArrow( colorRed, GetEyePosition(), GetEyePosition() + viewAxis[ 0 ] * 32.0f, 4, gameLocal.msec );
}
// Dynamically update the chest offset if a joint was specified
if ( chestOffsetJoint != INVALID_JOINT ) {
GetJointWorldTransform( chestOffsetJoint, gameLocal.time, chestOffset, axis );
chestOffset = ( chestOffset - GetPhysics()->GetOrigin() ) * viewAxis.Transpose();
}
if ( DebugFilter( ai_debugMove ) ) { // RED = Eye Pos & orientation
gameRenderWorld->DebugArrow( colorPink, GetChestPosition(), GetChestPosition() + viewAxis[ 0 ] * 32.0f, 4, gameLocal.msec );
}
return false;
}
/*
================
idActor::RemoveAttachments
================
*/
void idActor::RemoveAttachments( void ) {
int i;
idEntity *ent;
// remove any attached entities
for( i = 0; i < attachments.Num(); i++ ) {
ent = attachments[ i ].ent.GetEntity();
if ( ent && ent->spawnArgs.GetBool( "remove" ) ) {
ent->PostEventMS( &EV_Remove, 0 );
}
}
}
/*
================
idActor::Attach
================
*/
void idActor::Attach( idEntity *ent ) {
idVec3 origin;
idMat3 axis;
jointHandle_t joint;
idStr jointName;
idAttachInfo &attach = attachments.Alloc();
idAngles angleOffset;
idVec3 originOffset;
jointName = ent->spawnArgs.GetString( "joint" );
joint = animator.GetJointHandle( jointName );
if ( joint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for attaching '%s' on '%s'", jointName.c_str(), ent->GetClassname(), name.c_str() );
}
angleOffset = ent->spawnArgs.GetAngles( "angles" );
originOffset = ent->spawnArgs.GetVector( "origin" );
attach.channel = animator.GetChannelForJoint( joint );
GetJointWorldTransform( joint, gameLocal.time, origin, axis );
attach.ent = ent;
ent->SetOrigin( origin + originOffset * renderEntity.axis );
idMat3 rotate = angleOffset.ToMat3();
idMat3 newAxis = rotate * axis;
ent->SetAxis( newAxis );
ent->BindToJoint( this, joint, true );
ent->cinematic = cinematic;
}
idEntity* idActor::FindAttachment( const char* attachmentName )
{
idEntity *ent = NULL;
const char* fullName = va("idAFAttachment_%s",attachmentName);
// find the specified attachment
for( int i = 0; i < attachments.Num(); i++ ) {
ent = attachments[ i ].ent.GetEntity();
if ( ent && !ent->name.CmpPrefix(fullName) ) {
return ent;
}
}
return NULL;
}
void idActor::HideAttachment( const char* attachmentName )
{
idEntity *ent = FindAttachment( attachmentName );
if ( ent )
{
ent->Hide();
}
}
void idActor::ShowAttachment( const char* attachmentName )
{
idEntity *ent = FindAttachment( attachmentName );
if ( ent )
{
ent->Show();
}
}
/*
================
idActor::Teleport
================
*/
void idActor::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
GetPhysics()->SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
GetPhysics()->SetLinearVelocity( vec3_origin );
viewAxis = angles.ToMat3();
UpdateVisuals();
if ( !IsHidden() ) {
// kill anything at the new position
gameLocal.KillBox( this );
}
}
/*
================
idActor::GetDeltaViewAngles
================
*/
const idAngles &idActor::GetDeltaViewAngles( void ) const {
return deltaViewAngles;
}
/*
================
idActor::SetDeltaViewAngles
================
*/
void idActor::SetDeltaViewAngles( const idAngles &delta ) {
deltaViewAngles = delta;
}
/*
================
idActor::HasEnemies
================
*/
bool idActor::HasEnemies( void ) const {
idActor *ent;
for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
if ( !ent->fl.hidden ) {
return true;
}
}
return false;
}
/*
================
idActor::ClosestEnemyToPoint
================
*/
idActor *idActor::ClosestEnemyToPoint( const idVec3 &pos, float maxRange, bool returnFirst, bool checkPVS ) {
idActor *ent;
idActor *bestEnt;
float bestDistSquared;
float distSquared;
idVec3 delta;
pvsHandle_t pvs;
//just to supress the compiler warning
pvs.i = 0;
if ( checkPVS ) {
// Setup our local variables used in the search
pvs = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
}
bestDistSquared = maxRange?(maxRange*maxRange):idMath::INFINITY;
bestEnt = NULL;
for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
if ( ent->fl.hidden ) {
continue;
}
delta = ent->GetPhysics()->GetOrigin() - pos;
distSquared = delta.LengthSqr();
if ( distSquared < bestDistSquared ) {
if ( checkPVS ) {
// If this enemy isnt in the same pvps then use them as a backup
if ( pvs.i > 0
&& pvs.i < MAX_CURRENT_PVS
&& !gameLocal.pvs.InCurrentPVS( pvs, ent->GetPVSAreas(), ent->GetNumPVSAreas() ) ) {
continue;
}
}
bestEnt = ent;
bestDistSquared = distSquared;
if ( returnFirst ) {
break;
}
}
}
if ( checkPVS ) {
gameLocal.pvs.FreeCurrentPVS( pvs );
}
return bestEnt;
}
/*
================
idActor::EnemyWithMostHealth
================
*/
idActor *idActor::EnemyWithMostHealth() {
idActor *ent;
idActor *bestEnt;
int most = -9999;
bestEnt = NULL;
for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
if ( !ent->fl.hidden && ( ent->health > most ) ) {
bestEnt = ent;
most = ent->health;
}
}
return bestEnt;
}
/*
================
idActor::OnLadder
================
*/
bool idActor::OnLadder( void ) const {
return false;
}
/*
==============
idActor::GetAASLocation
==============
*/
void idActor::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
idVec3 size;
idBounds bounds;
GetFloorPos( 64.0f, pos );
if ( !aas ) {
areaNum = 0;
return;
}
size = aas->GetSettings()->boundingBoxes[0][1];
bounds[0] = -size;
size.z = 32.0f;
bounds[1] = size;
areaNum = aas->PointReachableAreaNum( pos, bounds, AREA_REACHABLE_WALK );
if ( areaNum ) {
aas->PushPointIntoAreaNum( areaNum, pos );
}
}
/***********************************************************************
animation state
***********************************************************************/
/*
=====================
idActor::StopAnimState
=====================
*/
void idActor::StopAnimState( int channel ) {
GetAnimState( channel ).Shutdown ( );
}
/*
=====================
idActor::PostAnimState
=====================
*/
void idActor::PostAnimState( int channel, const char* statename, int blendFrames, int delay, int flags ) {
GetAnimState( channel ).PostState( statename, blendFrames, delay, flags );
}
/*
=====================
idActor::SetAnimState
=====================
*/
void idActor::SetAnimState( int channel, const char *statename, int blendFrames, int flags ) {
switch ( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.GetStateThread().Clear();
break;
case ANIMCHANNEL_TORSO :
torsoAnim.GetStateThread().Clear();
legsAnim.Enable( blendFrames );
break;
case ANIMCHANNEL_LEGS :
legsAnim.GetStateThread().Clear();
torsoAnim.Enable( blendFrames );
break;
}
OnStateChange( channel );
PostAnimState( channel, statename, blendFrames, flags );
}
/*
=====================
idActor::OnStateChange
=====================
*/
void idActor::OnStateChange ( int channel ) {
allowEyeFocus = true;
// Only clear eye focus on head channel change
if ( channel == ANIMCHANNEL_HEAD ) {
return;
}
disablePain = false;
}
/*
=====================
idActor::OnFriendlyFire
=====================
*/
void idActor::OnFriendlyFire ( idActor* attacker ) {
}
/*
=====================
idActor::UpdateAnimState
=====================
*/
void idActor::UpdateAnimState( void ) {
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
MEM_SCOPED_TAG(tag,MA_ANIM);
// RAVEN END
headAnim.UpdateState();
torsoAnim.UpdateState();
legsAnim.UpdateState();
}
/*
=====================
idActor::GetAnim
=====================
*/
int idActor::GetAnim( int channel, const char *animname, bool forcePrefix ) {
int anim;
const char *temp;
idAnimator *animatorPtr;
if ( channel == ANIMCHANNEL_HEAD ) {
if ( !head.GetEntity() ) {
return 0;
}
animatorPtr = head.GetEntity()->GetAnimator();
} else {
animatorPtr = &animator;
}
// Allow for anim substitution
animname = spawnArgs.GetString ( va("anim %s", animname ), animname );
if ( animPrefix.Length() ) {
temp = va( "%s_%s", animPrefix.c_str(), animname );
anim = animatorPtr->GetAnim( temp );
if ( anim ) {
return anim;
} else if ( forcePrefix ) {
return NULL;
}
}
anim = animatorPtr->GetAnim( animname );
return anim;
}
/*
===============
idActor::SyncAnimChannels
===============
*/
void idActor::SyncAnimChannels( int channel, int syncToChannel, int blendFrames ) {
idAnimator *headAnimator;
idAFAttachment *headEnt;
int anim;
idAnimBlend *syncAnim;
int starttime;
int blendTime;
int cycle;
blendTime = FRAME2MS( blendFrames );
if ( channel == ANIMCHANNEL_HEAD ) {
headEnt = head.GetEntity();
if ( headEnt ) {
headAnimator = headEnt->GetAnimator();
syncAnim = animator.CurrentAnim( syncToChannel );
if ( syncAnim ) {
anim = headAnimator->GetAnim( syncAnim->AnimFullName() );
if ( !anim ) {
anim = headAnimator->GetAnim( syncAnim->AnimName() );
}
if ( anim ) {
cycle = animator.CurrentAnim( syncToChannel )->GetCycleCount();
starttime = animator.CurrentAnim( syncToChannel )->GetStartTime();
headAnimator->PlayAnim( ANIMCHANNEL_LEGS, anim, gameLocal.time, blendTime );
headAnimator->CurrentAnim( ANIMCHANNEL_LEGS )->SetCycleCount( cycle );
headAnimator->CurrentAnim( ANIMCHANNEL_LEGS )->SetStartTime( starttime );
} else {
headEnt->PlayIdleAnim( ANIMCHANNEL_LEGS, blendTime );
}
}
}
} else if ( syncToChannel == ANIMCHANNEL_HEAD ) {
headEnt = head.GetEntity();
if ( headEnt ) {
headAnimator = headEnt->GetAnimator();
syncAnim = headAnimator->CurrentAnim( ANIMCHANNEL_LEGS );
if ( syncAnim ) {
anim = GetAnim( channel, syncAnim->AnimFullName() );
if ( !anim ) {
anim = GetAnim( channel, syncAnim->AnimName() );
}
if ( anim ) {
cycle = headAnimator->CurrentAnim( ANIMCHANNEL_LEGS )->GetCycleCount();
starttime = headAnimator->CurrentAnim( ANIMCHANNEL_LEGS )->GetStartTime();
animator.PlayAnim( channel, anim, gameLocal.time, blendTime );
animator.CurrentAnim( channel )->SetCycleCount( cycle );
animator.CurrentAnim( channel )->SetStartTime( starttime );
}
}
}
} else {
animator.SyncAnimChannels( channel, syncToChannel, gameLocal.time, blendTime );
}
}
/***********************************************************************
Damage
***********************************************************************/
/*
============
idActor::Gib
============
*/
void idActor::Gib( const idVec3 &dir, const char *damageDefName ) {
// for multiplayer we use client-side models
if ( gameLocal.isMultiplayer ) {
return;
}
// only gib once
if ( gibbed ) {
return;
}
idAFEntity_Gibbable::Gib( dir, damageDefName );
if ( head.GetEntity() ) {
head.GetEntity()->Hide();
}
StopSound( SND_CHANNEL_VOICE, false );
gameLocal.PlayEffect ( spawnArgs, "fx_gib", GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
}
void idActor::CheckDeathObjectives( void )
{
idPlayer *player = gameLocal.GetLocalPlayer();
if ( !player || !player->GetObjectiveHud() ) {
return;
}
if ( spawnArgs.GetString( "objectivetitle_failed", NULL ) ) {
player->GetObjectiveHud()->SetStateString( "objective", "2" );
player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext_failed" ) ) );
player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle_failed" ) ) );
player->FailObjective( spawnArgs.GetString( "objectivetitle_failed" ) );
}
if ( spawnArgs.GetString( "objectivetitle_completed", NULL ) ) {
player->GetObjectiveHud()->SetStateString( "objective", "2" );
player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext_completed" ) ) );
player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle_completed" ) ) );
player->CompleteObjective( spawnArgs.GetString( "objectivetitle_completed" ) );
}
}
/*
============
idActor::Damage
this entity that is being damaged
inflictor entity that is causing the damage
attacker entity that caused the inflictor to damage targ
example: this=monster, inflictor=rocket, attacker=player
dir direction of the attack for knockback in global space
point point at which the damage is being inflicted, used for headshots
damage amount of damage being inflicted
inflictor, attacker, dir, and point can be NULL for environmental effects
Bleeding wounds and surface overlays are applied in the collision code that
calls Damage()
============
*/
void idActor::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
const char *damageDefName, const float damageScale, const int location ) {
if ( !fl.takedamage ) {
return;
}
if ( !inflictor ) {
inflictor = gameLocal.world;
}
if ( !attacker ) {
attacker = gameLocal.world;
}
const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName, false );
if ( !damageDef ) {
gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
}
int damage = damageDef->GetInt( "damage" ) * damageScale;
damage = GetDamageForLocation( damage, location );
// friendly fire damage
bool noDmgFeedback = false;
if ( attacker->IsType ( idActor::Type ) && static_cast<idActor*>(attacker)->team == team ) {
OnFriendlyFire ( static_cast<idActor*>(attacker) );
// jshepard:
// if the player deals friendly fire damage it is reduced to 0. If the damage is splash damage,
// then the victim should use a pain anim.
if( static_cast<idPlayer*>( attacker ) == gameLocal.GetLocalPlayer() ) {
//play pain (maybe one day a special anim?) for damages that have the cower keyword
if ( damageDef->GetBool( "cower" )) {
Pain( inflictor, attacker, damage, dir, location );
}
//reduce the damage
damage = 0;
noDmgFeedback = true;
}
// reduce friendly fire damage by the teamscale
damage = floor( damage * damageDef->GetFloat ( "teamScale", "0.5" ) );
}
if ( !IsType( idPlayer::GetClassType() ) && attacker->IsType( idAI::GetClassType() ) ) {
if ( ((idAI*)attacker)->aifl.killerGuard ) {
//Hard-coded to do lots of damage
damage = 100;
}
}
if ( !noDmgFeedback ) {
// inform the attacker that they hit someone
attacker->DamageFeedback( this, inflictor, damage );
}
// RAVEN BEGIN
// jnewquist: FIXME - Was this removed from Xenon intentionally?
#ifdef _XENON
// singleplayer stat reporting.
if(!gameLocal.isMultiplayer) {
int methodOfDeath = -1;
// jnewquist: Use accessor for static class type
if ( inflictor->IsType( idProjectile::GetClassType() ) ) {
// RAVEN END
methodOfDeath = static_cast<idProjectile*>(inflictor)->methodOfDeath;
} else if ( inflictor->IsType( idPlayer::GetClassType() ) ) {
// hitscan weapon
methodOfDeath = static_cast<idPlayer*>(inflictor)->GetCurrentWeapon();
}
if( methodOfDeath != -1 && attacker && attacker->IsType( idActor::Type ) ) {
// jnewquist: Fix Xenon compile warning
statManager->WeaponHit( static_cast<idActor*> (attacker) , this, methodOfDeath, !!damage );
}
}
#endif
// RAVEN END
// RAVEN BEGIN
// MCG - added damage over time
if ( !inDamageEvent ) {
if ( damageDef->GetFloat( "dot_duration" ) ) {
int endTime;
if ( damageDef->GetFloat( "dot_duration" ) == -1 ) {
endTime = -1;
} else {
endTime = gameLocal.GetTime() + SEC2MS(damageDef->GetFloat( "dot_duration" ));
}
int interval = SEC2MS(damageDef->GetFloat( "dot_interval", "0" ));
if ( endTime == -1 || gameLocal.GetTime() + interval <= endTime ) {//post it again
PostEventMS( &EV_DamageOverTime, interval, endTime, interval, inflictor, attacker, dir, damageDefName, damageScale, location );
}
if ( damageDef->GetString( "fx_dot", NULL ) ) {
ProcessEvent( &EV_DamageOverTimeEffect, endTime, interval, damageDefName );
}
if ( damageDef->GetString( "snd_dot_start", NULL ) ) {
StartSound ( "snd_dot_start", SND_CHANNEL_ANY, 0, false, NULL );
}
}
}
// RAVEN END
if ( damage > 0 ) {
int oldHealth = health;
AdjustHealthByDamage ( damage );
if ( health <= 0 ) {
//allow for quick burning
if (damageDef->GetFloat( "quickburn", "0" ) && !spawnArgs.GetFloat("no_quickburn", "0")) {
fl.quickBurn = true;
}
if ( health < -999 ) {
health = -999;
}
//annoying hack for StartRagdoll
bool saveGibbed = gibbed;
bool canDMG_Gib = (spawnArgs.GetBool( "gib" ) | spawnArgs.GetBool( "DMG_gib" ));
if ( health < -20 )
{
if ( (spawnArgs.GetBool( "gib" ) && damageDef->GetBool( "gib" )) ||
(canDMG_Gib && damageDef->GetBool( "DMG_gib")))
{
gibbed = true;
}
}
Killed( inflictor, attacker, damage, dir, location );
gibbed = saveGibbed;
if ( health < -20 )
{
if ( (spawnArgs.GetBool( "gib" ) && damageDef->GetBool( "gib" )) ||
(canDMG_Gib && damageDef->GetBool( "DMG_gib")))
{
Gib( dir, damageDefName );
}
}
if ( oldHealth > 0 && !gibbed && !fl.quickBurn) {
float pushScale = 1.0f;
if ( inflictor && inflictor->IsType ( idPlayer::GetClassType() ) ) {
pushScale = static_cast<idPlayer*>(inflictor)->PowerUpModifier ( PMOD_PROJECTILE_DEATHPUSH );
}
InitDeathPush ( dir, location, damageDef, pushScale );
}
} else {
painType = damageDef->GetString ( "pain" );
Pain( inflictor, attacker, damage, dir, location );
}
} else {
// don't accumulate knockback
/*
if ( af.IsLoaded() ) {
// clear impacts
af.Rest();
// physics is turned off by calli/ng af.Rest()
BecomeActive( TH_PHYSICS );
}
*/
}
}
/*
=====================
idActor::InitDeathPush
=====================
*/
void idActor::InitDeathPush ( const idVec3& dir, int location, const idDict* damageDict, float pushScale ) {
idVec2 forceMin;
idVec2 forceMax;
if( !af.IsActive() ) {
return;
}
if ( deathPushTime > gameLocal.time ) {
return;
}
if ( !damageDict->GetInt ( "deathPush", "0", deathPushTime ) || deathPushTime <= 0 ) {
return;
}
damageDict->GetVec2( "deathPushMin", "", forceMin );
damageDict->GetVec2( "deathPushMax", "", forceMax );
/*
forceMin *= (pushScale * GetPhysics()->GetMass());
forceMax *= (pushScale * GetPhysics()->GetMass());
*/
forceMin *= (pushScale);
forceMax *= (pushScale);
deathPushForce = dir;
deathPushForce.Normalize ( );
deathPushForce = rvRandom::flrand ( forceMin.x, forceMax.x ) * deathPushForce +
-rvRandom::flrand ( forceMin.y, forceMax.y ) * GetPhysics()->GetGravityNormal ( );
deathPushTime += gameLocal.time;
deathPushJoint = (jointHandle_t) location;
}
/*
=====================
idActor::DeathPush
=====================
*/
void idActor::DeathPush ( void ) {
if ( deathPushTime <= gameLocal.time ) {
return;
}
idVec3 center;
center = GetPhysics()->GetAbsBounds ( ).GetCenter();
GetPhysics()->ApplyImpulse ( 0, center, -0.5f * GetPhysics()->GetMass () * MS2SEC(gameLocal.GetMSec()) * GetPhysics()->GetGravity ( ) );
if ( deathPushJoint != INVALID_JOINT ) {
idVec3 origin;
idMat3 axis;
GetJointWorldTransform ( deathPushJoint, gameLocal.time, origin, axis );
GetPhysics()->ApplyImpulse ( 0, origin, deathPushForce );
} else {
GetPhysics()->ApplyImpulse ( 0, center, deathPushForce );
}
}
/*
=====================
idActor::SkipImpulse
=====================
*/
bool idActor::SkipImpulse( idEntity* ent, int id ) {
return idAFEntity_Gibbable::SkipImpulse( ent, id ) || health <= 0 || gibbed || ent->IsType( idActor::GetClassType() ) || ent->IsType( idProjectile::GetClassType() );
}
/*
=====================
idActor::AddDamageEffect
=====================
*/
void idActor::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ) {
if ( !gameLocal.isMultiplayer && inflictor && inflictor->IsType ( idActor::GetClassType ( ) ) ) {
if ( static_cast<idActor*>(inflictor)->team == team ) {
return;
}
}
idAFEntity_Gibbable::AddDamageEffect( collision, velocity, damageDefName, inflictor );
}
/*
=====================
idActor::ClearPain
=====================
*/
void idActor::ClearPain( void ) {
pain_debounce_time = 0;
}
/*
=====================
idActor::Pain
=====================
*/
bool idActor::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
if ( af.IsLoaded() ) {
// clear impacts
af.Rest();
// physics is turned off by calling af.Rest()
BecomeActive( TH_PHYSICS );
}
if ( gameLocal.time < pain_debounce_time ) {
return false;
}
// No pain if being hit by a friendly target
// jshepard: friendly targets can now cause pain
/*
if ( attacker && attacker->IsType ( idActor::GetClassType ( ) ) ) {
if ( static_cast<idActor*>( attacker )->team == team ) {
return false;
}
}
*/
// don't play pain sounds more than necessary
pain_debounce_time = gameLocal.time + pain_delay;
float f;
// RAVEN BEGIN
// mekberg: fixed divide by zero
float spawnHealth = spawnArgs.GetFloat ( "health", "1" );
if (spawnHealth<1.0f) {
spawnHealth = 1.0f; // more devide by zero nonsense
}
f = (float)damage / spawnHealth;
// RAVEN END
if( gameLocal.isMultiplayer && IsType( idPlayer::GetClassType() ) && (health < 0.25f * ((idPlayer*)this)->inventory.maxHealth) ) {
StartSound( "snd_pain_low_health", SND_CHANNEL_VOICE, 0, false, NULL );
} else {
if ( f > 0.75f ) {
StartSound( "snd_pain_huge", SND_CHANNEL_VOICE, 0, false, NULL );
} else if ( f > 0.5f ) {
StartSound( "snd_pain_large", SND_CHANNEL_VOICE, 0, false, NULL );
} else if ( f > 0.25f ) {
StartSound( "snd_pain_medium", SND_CHANNEL_VOICE, 0, false, NULL );
} else {
StartSound( "snd_pain_small", SND_CHANNEL_VOICE, 0, false, NULL );
}
}
if ( disablePain || ( gameLocal.time < painTime ) ) {
// don't play a pain anim
return false;
}
// set the pain anim
idStr damageGroup = GetDamageGroup( location );
painAnim.Clear ( );
// If we have both a damage group and a pain type then check that combination first
if ( damageGroup.Length ( ) && painType.Length ( ) ) {
painAnim = va ( "pain_%s_%s", painType.c_str(), damageGroup.c_str() );
if ( !animator.HasAnim ( painAnim ) ) {
painAnim.Clear ( );
}
}
// Do we have a pain anim for just the pain type?
if ( !painAnim.Length ( ) && painType.Length ( ) ) {
painAnim = va ( "pain_%s", painType.c_str() );
if ( !animator.HasAnim ( painAnim ) ) {
painAnim.Clear ( );
}
}
// Do we have a pain anim for just the damage group?
if ( !painAnim.Length ( ) && damageGroup.Length ( ) ) {
painAnim = va ( "pain_%s", damageGroup.c_str() );
if ( !animator.HasAnim ( painAnim ) ) {
painAnim.Clear ( );
}
}
if ( !painAnim.Length() ) {
painAnim = "pain";
}
if ( g_debugDamage.GetBool() ) {
gameLocal.Printf( "Damage: joint: '%s', zone '%s', anim '%s'\n", animator.GetJointName( ( jointHandle_t )location ),
damageGroup.c_str(), painAnim.c_str() );
}
return true;
}
/*
=====================
idActor::SpawnGibs
=====================
*/
void idActor::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
idAFEntity_Gibbable::SpawnGibs( dir, damageDefName );
RemoveAttachments();
}
/*
=====================
idActor::SetupDamageGroups
FIXME: only store group names once and store an index for each joint
=====================
*/
void idActor::SetupDamageGroups( void ) {
int i;
const idKeyValue *arg;
idStr groupname;
idList<jointHandle_t> jointList;
int jointnum;
float scale;
// create damage zones
damageGroups.SetNum( animator.NumJoints() );
arg = spawnArgs.MatchPrefix( "damage_zone ", NULL );
while ( arg ) {
groupname = arg->GetKey();
groupname.Strip( "damage_zone " );
animator.GetJointList( arg->GetValue(), jointList );
for( i = 0; i < jointList.Num(); i++ ) {
jointnum = jointList[ i ];
damageGroups[ jointnum ] = groupname;
}
jointList.Clear();
arg = spawnArgs.MatchPrefix( "damage_zone ", arg );
}
// initilize the damage zones to normal damage
damageScale.SetNum( animator.NumJoints() );
for( i = 0; i < damageScale.Num(); i++ ) {
damageScale[ i ] = 1.0f;
}
// set the percentage on damage zones
arg = spawnArgs.MatchPrefix( "damage_scale ", NULL );
while ( arg ) {
scale = atof( arg->GetValue() );
groupname = arg->GetKey();
groupname.Strip( "damage_scale " );
for( i = 0; i < damageScale.Num(); i++ ) {
if ( damageGroups[ i ] == groupname ) {
damageScale[ i ] = scale;
}
}
arg = spawnArgs.MatchPrefix( "damage_scale ", arg );
}
}
/*
=====================
idActor::GetDamageForLocation
=====================
*/
int idActor::GetDamageForLocation( int damage, int location ) {
if ( ( location < 0 ) || ( location >= damageScale.Num() ) ) {
return damage;
}
return (int)ceil( damage * damageScale[ location ] );
}
/*
=====================
idActor::GetDamageGroup
=====================
*/
const char *idActor::GetDamageGroup( int location ) {
if ( ( location < 0 ) || ( location >= damageGroups.Num() ) ) {
return "";
}
return damageGroups[ location ];
}
// RAVEN BEGIN
// bdube: added for vehicle
/*
==============
idActor::ExitVehicle
==============
*/
bool idActor::ExitVehicle ( bool force ) {
idMat3 axis;
idVec3 origin;
if ( !IsInVehicle ( ) ) {
return false;
}
if ( vehicleController.GetVehicle()->IsLocked() ) {
if ( force ) {
vehicleController.GetVehicle()->Unlock();
} else {
return false;
}
}
if( !vehicleController.FindClearExitPoint(origin, axis) ) {
if ( force ) {
origin = GetPhysics()->GetOrigin() + idVec3( spawnArgs.GetVector( "forced_exit_offset", "-100 0 0" ) );
axis = GetPhysics()->GetAxis();
} else {
return false;
}
}
vehicleController.Eject ( force );
GetPhysics()->SetOrigin( origin );
viewAxis = axis[0].ToMat3();
GetPhysics()->SetAxis( mat3_identity );
GetPhysics()->SetLinearVelocity( vec3_origin );
return true;
}
/*
=====================
idActor::EnterVehicle
=====================
*/
bool idActor::EnterVehicle ( idEntity* ent ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( IsInVehicle ( ) || !ent->IsType ( rvVehicle::GetClassType() ) ) {
// RAVEN END
return false ;
}
// Get in the vehicle
if ( !vehicleController.Drive ( static_cast<rvVehicle*>(ent), this ) ) {
return false;
}
return true;
}
// RAVEN END
/***********************************************************************
Events
***********************************************************************/
/*
=====================
idActor::FootStep
=====================
*/
void idActor::FootStep( void ) {
const char* sound;
const rvDeclMatType* materialType;
if ( !GetPhysics()->HasGroundContacts() ) {
return;
}
// start footstep sound based on material type
materialType = GetPhysics()->GetContact( 0 ).materialType;
sound = NULL;
// Sound based on material type?
if ( materialType ) {
sound = spawnArgs.GetString( va( "snd_footstep_%s", materialType->GetName() ) );
}
if ( !sound || !*sound ) {
sound = spawnArgs.GetString( "snd_footstep" );
}
// If we have a sound then play it
if ( sound && *sound ) {
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
}
}
/*
=====================
idActor::Event_EnableEyeFocus
=====================
*/
void idActor::Event_EnableEyeFocus( void ) {
allowEyeFocus = true;
blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
}
/*
=====================
idActor::Event_DisableEyeFocus
=====================
*/
void idActor::Event_DisableEyeFocus( void ) {
allowEyeFocus = false;
idEntity *headEnt = head.GetEntity();
if ( headEnt ) {
headEnt->GetAnimator()->Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
} else {
animator.Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
}
}
/*
=====================
idActor::Event_EnableBlink
=====================
*/
void idActor::Event_EnableBlink( void ) {
blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
}
/*
=====================
idActor::Event_DisableBlink
=====================
*/
void idActor::Event_DisableBlink( void ) {
blink_time = 0x7FFFFFFF;
}
/*
===============
idActor::Event_Footstep
===============
*/
void idActor::Event_Footstep( void ) {
FootStep ( );
}
/*
=====================
idActor::Event_EnableWalkIK
=====================
*/
void idActor::Event_EnableWalkIK( void ) {
walkIK.EnableAll();
}
/*
=====================
idActor::Event_DisableWalkIK
=====================
*/
void idActor::Event_DisableWalkIK( void ) {
walkIK.DisableAll();
}
/*
=====================
idActor::Event_EnableLegIK
=====================
*/
void idActor::Event_EnableLegIK( int num ) {
walkIK.EnableLeg( num );
}
/*
=====================
idActor::Event_DisableLegIK
=====================
*/
void idActor::Event_DisableLegIK( int num ) {
walkIK.DisableLeg( num );
}
/*
=====================
idActor::Event_PreventPain
=====================
*/
void idActor::Event_PreventPain( float duration ) {
painTime = gameLocal.time + SEC2MS( duration );
}
/*
===============
idActor::Event_DisablePain
===============
*/
void idActor::Event_DisablePain( void ) {
// RAVEN BEGIN
// bdube: reversed var
disablePain = true;
// RAVEN END
}
/*
===============
idActor::Event_EnablePain
===============
*/
void idActor::Event_EnablePain( void ) {
// RAVEN BEGIN
// bdube: reversed var
disablePain = false;
// RAVEN END
}
/*
=====================
idActor::Event_SetAnimPrefix
=====================
*/
void idActor::Event_SetAnimPrefix( const char *prefix ) {
animPrefix = prefix;
}
/*
===============
idActor::Event_StopAnim
===============
*/
void idActor::Event_StopAnim( int channel, int frames ) {
switch( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.StopAnim( frames );
break;
case ANIMCHANNEL_TORSO :
torsoAnim.StopAnim( frames );
break;
case ANIMCHANNEL_LEGS :
legsAnim.StopAnim( frames );
break;
default:
gameLocal.Error( "Unknown anim group" );
break;
}
}
/*
===============
idActor::Event_PlayAnim
===============
*/
void idActor::Event_PlayAnim( int channel, const char *animname ) {
idThread::ReturnFloat( MS2SEC(PlayAnim(channel, animname, -1)) );
}
/*
===============
idActor::Event_PlayCycle
===============
*/
void idActor::Event_PlayCycle( int channel, const char *animname ) {
PlayCycle ( channel, animname, -1 );
idThread::ReturnInt( true );
}
/*
=====================
idAI::DebugFilter
=====================
*/
bool idActor::DebugFilter ( const idCVar& test ) const {
return ( health>0 && (test.GetBool() || test.GetInteger()>0) && (!ai_debugFilterString.GetString()[0] || !stricmp( name.c_str(), ai_debugFilterString.GetString() )));
}
/*
===============
idActor::Event_IdleAnim
===============
*/
void idActor::Event_IdleAnim( int channel, const char *animname ) {
int anim;
anim = GetAnim( channel, animname );
if ( !anim ) {
if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
} else {
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
}
switch( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.BecomeIdle();
break;
case ANIMCHANNEL_TORSO :
torsoAnim.BecomeIdle();
break;
case ANIMCHANNEL_LEGS :
legsAnim.BecomeIdle();
break;
default:
gameLocal.Error( "Unknown anim group" );
}
idThread::ReturnInt( false );
return;
}
switch( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.BecomeIdle();
if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
// don't sync to torso body if it doesn't override idle anims
headAnim.CycleAnim( anim );
} else if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
// everything is idle, so play the anim on the head and copy it to the torso and legs
headAnim.CycleAnim( anim );
torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
} else if ( torsoAnim.IsIdle() ) {
// sync the head and torso to the legs
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, headAnim.animBlendFrames );
torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
} else {
// sync the head to the torso
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, headAnim.animBlendFrames );
}
break;
case ANIMCHANNEL_TORSO :
torsoAnim.BecomeIdle();
if ( legsAnim.GetAnimFlags().prevent_idle_override ) {
// don't sync to legs if legs anim doesn't override idle anims
torsoAnim.CycleAnim( anim );
} else if ( legsAnim.IsIdle() ) {
// play the anim in both legs and torso
torsoAnim.CycleAnim( anim );
legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
} else {
// sync the anim to the legs
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
}
if ( headAnim.IsIdle() ) {
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
}
break;
case ANIMCHANNEL_LEGS :
legsAnim.BecomeIdle();
if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
// don't sync to torso if torso anim doesn't override idle anims
legsAnim.CycleAnim( anim );
} else if ( torsoAnim.IsIdle() ) {
// play the anim in both legs and torso
legsAnim.CycleAnim( anim );
torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
if ( headAnim.IsIdle() ) {
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
}
} else {
// sync the anim to the torso
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, legsAnim.animBlendFrames );
}
break;
default:
gameLocal.Error( "Unknown anim group" );
}
idThread::ReturnInt( true );
}
/*
================
idActor::Event_SetSyncedAnimWeight
================
*/
void idActor::Event_SetSyncedAnimWeight( int channel, int anim, float weight ) {
idEntity *headEnt;
headEnt = head.GetEntity();
switch( channel ) {
case ANIMCHANNEL_HEAD :
if ( headEnt ) {
animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
} else {
animator.CurrentAnim( ANIMCHANNEL_HEAD )->SetSyncedAnimWeight( anim, weight );
}
if ( torsoAnim.IsIdle() ) {
animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
if ( legsAnim.IsIdle() ) {
animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
}
}
break;
case ANIMCHANNEL_TORSO :
animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
if ( legsAnim.IsIdle() ) {
animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
}
if ( headEnt && headAnim.IsIdle() ) {
headEnt->GetAnimator()->CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
}
break;
case ANIMCHANNEL_LEGS :
animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
if ( torsoAnim.IsIdle() ) {
animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
if ( headEnt && headAnim.IsIdle() ) {
headEnt->GetAnimator()->CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
}
}
break;
default:
gameLocal.Error( "Unknown anim group" );
}
}
/*
===============
idActor::Event_OverrideAnim
===============
*/
void idActor::Event_OverrideAnim( int channel ) {
switch( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.Disable();
if ( !torsoAnim.IsIdle() ) {
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
} else {
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
}
break;
case ANIMCHANNEL_TORSO :
torsoAnim.Disable();
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
if ( headAnim.IsIdle() ) {
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
}
break;
case ANIMCHANNEL_LEGS :
legsAnim.Disable();
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
break;
default:
gameLocal.Error( "Unknown anim group" );
break;
}
}
/*
===============
idActor::Event_EnableAnim
===============
*/
void idActor::Event_EnableAnim( int channel, int blendFrames ) {
switch( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.Enable( blendFrames );
break;
case ANIMCHANNEL_TORSO :
torsoAnim.Enable( blendFrames );
break;
case ANIMCHANNEL_LEGS :
legsAnim.Enable( blendFrames );
break;
default:
gameLocal.Error( "Unknown anim group" );
break;
}
}
/*
===============
idActor::Event_SetBlendFrames
===============
*/
void idActor::Event_SetBlendFrames( int channel, int blendFrames ) {
switch( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.animBlendFrames = blendFrames;
headAnim.lastAnimBlendFrames = blendFrames;
break;
case ANIMCHANNEL_TORSO :
torsoAnim.animBlendFrames = blendFrames;
torsoAnim.lastAnimBlendFrames = blendFrames;
break;
case ANIMCHANNEL_LEGS :
legsAnim.animBlendFrames = blendFrames;
legsAnim.lastAnimBlendFrames = blendFrames;
break;
default:
gameLocal.Error( "Unknown anim group" );
break;
}
}
/*
===============
idActor::Event_GetBlendFrames
===============
*/
void idActor::Event_GetBlendFrames( int channel ) {
switch( channel ) {
case ANIMCHANNEL_HEAD :
idThread::ReturnInt( headAnim.animBlendFrames );
break;
case ANIMCHANNEL_TORSO :
idThread::ReturnInt( torsoAnim.animBlendFrames );
break;
case ANIMCHANNEL_LEGS :
idThread::ReturnInt( legsAnim.animBlendFrames );
break;
default:
gameLocal.Error( "Unknown anim group" );
break;
}
}
/*
================
idActor::Event_HasEnemies
================
*/
void idActor::Event_HasEnemies( void ) {
bool hasEnemy;
hasEnemy = HasEnemies();
idThread::ReturnInt( hasEnemy );
}
/*
================
idActor::Event_NextEnemy
================
*/
void idActor::Event_NextEnemy( idEntity *ent ) {
idActor *actor;
if ( !ent || ( ent == this ) ) {
actor = enemyList.Next();
} else {
if ( !ent->IsType( idActor::Type ) ) {
gameLocal.Error( "'%s' cannot be an enemy", ent->name.c_str() );
}
actor = static_cast<idActor *>( ent );
if ( actor->enemyNode.ListHead() != &enemyList ) {
gameLocal.Error( "'%s' is not in '%s' enemy list", actor->name.c_str(), name.c_str() );
}
}
for( ; actor != NULL; actor = actor->enemyNode.Next() ) {
if ( !actor->fl.hidden ) {
idThread::ReturnEntity( actor );
return;
}
}
idThread::ReturnEntity( NULL );
}
/*
================
idActor::Event_ClosestEnemyToPoint
================
*/
void idActor::Event_ClosestEnemyToPoint( const idVec3 &pos ) {
idActor *bestEnt = ClosestEnemyToPoint( pos );
idThread::ReturnEntity( bestEnt );
}
/*
================
idActor::Event_StopSound
================
*/
void idActor::Event_StopSound( int channel, int netSync ) {
if ( channel == SND_CHANNEL_VOICE ) {
idEntity *headEnt = head.GetEntity();
if ( headEnt ) {
headEnt->StopSound( channel, ( netSync != 0 ) );
}
}
StopSound( channel, ( netSync != 0 ) );
}
/*
=====================
idActor::Event_GetHead
=====================
*/
void idActor::Event_GetHead( void ) {
idThread::ReturnEntity( head.GetEntity() );
}
// RAVEN BEGIN
// jshepard: added
/*
=====================
idActor::Event_SetAnimRate
=====================
*/
void idActor::Event_SetAnimRate( float multiplier ) {
animator.SetPlaybackRate(multiplier);
}
/*
===============================================================================
Wait States
===============================================================================
*/
/*
================
idActor::State_Wait_Frame
Stop a state thread for a single frame
================
*/
stateResult_t idActor::State_Wait_Frame ( const stateParms_t& parms ) {
return SRESULT_DONE_WAIT;
}
/*
================
idActor::State_Wait_LegsAnim
Stop a state thread until the animation running on the legs channel is finished
================
*/
stateResult_t idActor::State_Wait_LegsAnim ( const stateParms_t& parms ) {
if ( !AnimDone ( ANIMCHANNEL_LEGS, parms.blendFrames ) ) {
return SRESULT_WAIT;
}
return SRESULT_DONE;
}
/*
================
idActor::State_Wait_TorsoAnim
Stop a state thread until the animation running on the torso channel is finished
================
*/
stateResult_t idActor::State_Wait_TorsoAnim ( const stateParms_t& parms ) {
if ( !AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
return SRESULT_WAIT;
}
return SRESULT_DONE;
}
/*
================
idActor::PlayAnim
================
*/
int idActor::PlayAnim ( int channel, const char *animname, int blendFrames ) {
animFlags_t flags;
idEntity *headEnt;
int anim;
if ( blendFrames != -1 ) {
Event_SetBlendFrames ( channel, blendFrames );
}
anim = GetAnim( channel, animname );
if( ai_animShow.GetBool() ){
gameLocal.DPrintf( "Playing animation '%s' on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "head", "" ) );
}
if ( !anim ) {
if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
} else {
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
}
return 0;
}
switch( channel ) {
case ANIMCHANNEL_HEAD :
headEnt = head.GetEntity();
if ( headEnt ) {
headAnim.idleAnim = false;
headAnim.PlayAnim( anim );
flags = headAnim.GetAnimFlags();
if ( !flags.prevent_idle_override ) {
if ( torsoAnim.IsIdle() ) {
torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
if ( legsAnim.IsIdle() ) {
legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
}
}
}
}
break;
case ANIMCHANNEL_TORSO :
torsoAnim.idleAnim = false;
torsoAnim.PlayAnim( anim );
flags = torsoAnim.GetAnimFlags();
if ( !flags.prevent_idle_override ) {
if ( headAnim.IsIdle() ) {
headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
}
if ( legsAnim.IsIdle() ) {
legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
}
}
break;
case ANIMCHANNEL_LEGS :
legsAnim.idleAnim = false;
legsAnim.PlayAnim( anim );
flags = legsAnim.GetAnimFlags();
if ( !flags.prevent_idle_override ) {
if ( torsoAnim.IsIdle() ) {
torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
if ( headAnim.IsIdle() ) {
headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
}
}
}
break;
default :
gameLocal.Error( "Unknown anim group" );
break;
}
return animator.CurrentAnim( channel )->Length();
}
/*
================
idActor::PlayCycle
================
*/
bool idActor::PlayCycle ( int channel, const char *animname, int blendFrames ) {
animFlags_t flags;
int anim;
if ( blendFrames != -1 ) {
Event_SetBlendFrames ( channel, blendFrames );
}
anim = GetAnim( channel, animname );
if ( !anim ) {
if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
} else {
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
}
return false;
}
switch( channel ) {
case ANIMCHANNEL_HEAD :
headAnim.idleAnim = false;
headAnim.CycleAnim( anim );
flags = headAnim.GetAnimFlags();
if ( !flags.prevent_idle_override ) {
if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
}
}
break;
case ANIMCHANNEL_TORSO :
torsoAnim.idleAnim = false;
torsoAnim.CycleAnim( anim );
flags = torsoAnim.GetAnimFlags();
if ( !flags.prevent_idle_override ) {
if ( headAnim.IsIdle() ) {
headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
}
if ( legsAnim.IsIdle() ) {
legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
}
}
break;
case ANIMCHANNEL_LEGS :
legsAnim.idleAnim = false;
legsAnim.CycleAnim( anim );
flags = legsAnim.GetAnimFlags();
if ( !flags.prevent_idle_override ) {
if ( torsoAnim.IsIdle() ) {
torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
if ( headAnim.IsIdle() ) {
headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
}
}
}
break;
default:
gameLocal.Error( "Unknown anim group" );
}
return true;
}
void idActor::IdleAnim ( int channel, const char *name, int blendFrames ) {
Event_SetBlendFrames ( channel, blendFrames );
Event_IdleAnim ( channel, name );
}
void idActor::OverrideAnim ( int channel ) {
Event_OverrideAnim ( channel );
}
idAnimState& idActor::GetAnimState ( int channel ) {
switch ( channel ) {
case ANIMCHANNEL_LEGS: return legsAnim;
case ANIMCHANNEL_TORSO: return torsoAnim;
case ANIMCHANNEL_HEAD: return headAnim;
default:
gameLocal.Error( "idActor::GetAnimState: Unknown anim channel" );
return torsoAnim;
}
}
void idActor::DisableAnimState ( int channel ) {
Event_OverrideAnim ( channel );
// GetAnimState ( channel ).Disable ( );
}
void idActor::EnableAnimState ( int channel ) {
GetAnimState ( channel ).Enable ( 4 );
}
bool idActor::HasAnim ( int channel, const char* animname, bool forcePrefix ) {
return GetAnim( channel, animname, forcePrefix ) != NULL;
}
bool idActor::AnimDone ( int channel, int blendFrames ) {
return GetAnimState( channel ).AnimDone ( blendFrames );
}
/*
=====================
idActor::GetDebugInfo
=====================
*/
void idActor::GetDebugInfo ( debugInfoProc_t proc, void* userData ) {
// Base class first
idAFEntity_Gibbable::GetDebugInfo ( proc, userData );
proc ( "idActor", "state", stateThread.GetState()?stateThread.GetState()->state->name : "<none>", userData );
proc ( "idActor", "legs_state", legsAnim.GetStateThread().GetState()?legsAnim.GetStateThread().GetState()->state->name:"<none>", userData );
proc ( "idActor", "legs_disable", legsAnim.Disabled()?"true":"false", userData );
proc ( "idActor", "legs_anim", GetAnimator()->CurrentAnim ( ANIMCHANNEL_LEGS ) ? GetAnimator()->CurrentAnim ( ANIMCHANNEL_LEGS )->AnimName ( ) : "<none>", userData );
proc ( "idActor", "torso_state", torsoAnim.GetStateThread().GetState()?torsoAnim.GetStateThread().GetState()->state->name:"<none>", userData );
proc ( "idActor", "torso_disabled", torsoAnim.Disabled()?"true":"false", userData );
proc ( "idActor", "torso_anim", GetAnimator()->CurrentAnim ( ANIMCHANNEL_TORSO ) ? GetAnimator()->CurrentAnim ( ANIMCHANNEL_TORSO )->AnimName ( ) : "<none>", userData );
proc ( "idActor", "head_state", headAnim.GetStateThread().GetState()?headAnim.GetStateThread().GetState()->state->name:"<none>", userData );
proc ( "idActor", "head_disabled", headAnim.Disabled()?"true":"false", userData );
proc ( "idActor", "head_anim", GetAnimator()->CurrentAnim ( ANIMCHANNEL_HEAD ) ? GetAnimator()->CurrentAnim ( ANIMCHANNEL_HEAD )->AnimName ( ) : "<none>", userData );
proc ( "idActor", "painAnim", painAnim.c_str(), userData );
proc ( "idActor", "animPrefix", animPrefix.c_str(), userData );
}
//MCG: damage over time
void idActor::Event_DamageOverTime ( int endTime, int interval, idEntity *inflictor, idEntity *attacker, idVec3 &dir,
const char *damageDefName, const float damageScale, int location ) {
const idDeclEntityDef* damageDef = gameLocal.FindEntityDef( damageDefName, false );
if ( damageDef ) {
inDamageEvent = true;
Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
inDamageEvent = false;
if ( endTime == -1 || gameLocal.GetTime() + interval <= endTime ) {
//post it again
PostEventMS( &EV_DamageOverTime, interval, endTime, interval, inflictor, attacker, dir, damageDefName, damageScale, location );
}
}
}
void idActor::Event_DamageOverTimeEffect ( int endTime, int interval, const char *damageDefName ) {
const idDeclEntityDef* damageDef = gameLocal.FindEntityDef( damageDefName, false );
if ( damageDef ) {
rvClientCrawlEffect* effect;
effect = new rvClientCrawlEffect ( gameLocal.GetEffect ( damageDef->dict, "fx_dot" ), this, interval );
effect->Play ( gameLocal.time, false );
if ( endTime == -1 || gameLocal.GetTime() + interval <= endTime ) {
//post it again
PostEventMS( &EV_DamageOverTimeEffect, interval, endTime, interval, damageDefName );
}
}
}
// MCG: script-callable joint crawl effect
void idActor::Event_JointCrawlEffect ( const char *effectKeyName, float crawlSecs ) {
if ( effectKeyName ) {
rvClientCrawlEffect* effect;
effect = new rvClientCrawlEffect( gameLocal.GetEffect ( spawnArgs, effectKeyName ), this, 100 );
effect->Play ( gameLocal.GetTime(), false );
crawlSecs -= 0.1f;
if ( crawlSecs >= 0.1f ) {
PostEventMS( &EV_JointCrawlEffect, 100, effectKeyName, crawlSecs );
}
}
}
idEntity* idActor::GetGroundElevator( idEntity* testElevator ) const {
idEntity* groundEnt = GetGroundEntity();
if ( !groundEnt ) {
return NULL;
}
while ( groundEnt->GetBindMaster() ) {
groundEnt = groundEnt->GetBindMaster();
}
if ( !groundEnt->IsType( idElevator::GetClassType() ) ) {
return NULL;
}
if ( testElevator && groundEnt != testElevator ) {
return groundEnt;
}
idEntity* traceEnt;
idVec3 testPoint = GetPhysics()->GetOrigin();
idVec3 testBottom;
testPoint.z += 1.0f;
for ( int x = 0; x < 2; x++ ) {
testPoint.x = GetPhysics()->GetAbsBounds()[x].x;
for ( int y = 0; y < 2; y++ ) {
testPoint.y = GetPhysics()->GetAbsBounds()[y].y;
testBottom = testPoint;
testBottom.z -= 65.0f;
trace_t tr;
gameLocal.TracePoint( this, tr, testPoint, testBottom, GetPhysics()->GetClipMask(), this );
traceEnt = gameLocal.FindEntity( tr.c.entityNum );
if ( !traceEnt ) {
return NULL;
}
while ( traceEnt->GetBindMaster() ) {
traceEnt = traceEnt->GetBindMaster();
}
if ( traceEnt != groundEnt ) {
return traceEnt;
}
if ( testElevator && traceEnt != testElevator ) {
return traceEnt;
}
}
}
return groundEnt;
}
void idActor::GuidedProjectileIncoming( idGuidedProjectile *projectile )
{
if ( IsInVehicle() )
{
if ( vehicleController.GetVehicle() )
{
vehicleController.GetVehicle()->GuidedProjectileIncoming( projectile );
}
}
}
// RAVEN END