ef2gamesource/dlls/game/actor_headwatcher.cpp

366 lines
8.4 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /Code/DLLs/game/actor_headwatcher.cpp $
// $Revision:: 21 $
// $Author:: Sketcher $
// $Date:: 5/04/03 5:49p $
//
// Copyright (C) 2001 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
//
#include "_pch_cpp.h"
#include "actor_enemymanager.h"
#include "player.h"
#include "object.h"
HeadWatcher::HeadWatcher()
{
// Should always use other constructor
gi.Error( ERR_FATAL, "HeadWatcher::HeadWatcher -- Default Constructor Called" );
}
HeadWatcher::HeadWatcher( Actor *actor )
{
//Initialize our Actor
if ( actor )
act = actor;
else
gi.Error( ERR_DROP, "HeadWatcher::HeadWatcher -- actor is NULL" );
_init();
}
HeadWatcher::~HeadWatcher()
{
}
void HeadWatcher::_init()
{
act->SetControllerTag( ACTOR_HEAD_TAG, gi.Tag_NumForName( act->edict->s.modelindex, "Bip01 Head" ) );
_currentHeadAngles = act->GetControllerAngles( ACTOR_HEAD_TAG );
_watchTarget = NULL;
// Numbers here for testing, should come from events so they can be set via tiki or script
_maxHeadTurnSpeed = 30.0f;
_turnThreshold = 0.0f;
_maxHeadYaw = 60.0f;
_maxHeadPitch = 45.0f;
_twitchHead = false;
_nextTwitchHeadTime = 0.0f;
_maxDistance = -1.0;
_explicitSet = false;
_ignoreWatchTarget = false;
}
void HeadWatcher::SetWatchTarget( Entity *ent )
{
_watchTarget = ent;
_explicitSet = true;
}
void HeadWatcher::SetWatchTarget( const str &targetName )
{
TargetList *tlist;
int numObjects;
tlist = world->GetTargetList( targetName );
numObjects = tlist->list.NumObjects();
if ( numObjects == 0 )
{
//Hey, No targets with that name
gi.WDPrintf( "No target with target name %s specified\n", targetName.c_str() );
return;
}
else if ( numObjects > 1 )
{
//Uh Oh... We have more than one target... Let's throw up an error
gi.WDPrintf( "More than one target with target name %s specified, grabbing first one\n", targetName.c_str() );
}
_watchTarget = tlist->list.ObjectAt( 1 );
_explicitSet = true;
}
void HeadWatcher::SetWatchSpeed( float speed )
{
_maxHeadTurnSpeed = speed;
}
void HeadWatcher::ClearWatchTarget()
{
_watchTarget = NULL;
_explicitSet = false;
}
Entity *HeadWatcher::GetWatchTarget()
{
return _watchTarget;
}
void HeadWatcher::HeadWatchTarget()
{
int tagNum;
Vector tagPos;
Vector watchPosition;
Actor *actTarget;
actTarget = NULL;
tagNum = gi.Tag_NumForName( act->edict->s.modelindex, "Bip01 Head" );
if ( tagNum < 0 )
return;
//Check if we even have an animation set yet
if ( act->animate->CurrentAnim(legs) < 0 )
return;
act->GetTag( "Bip01 Head", &tagPos );
if ( !_watchTarget || _ignoreWatchTarget )
{
if ( _twitchHead )
{
twitchHead();
return;
}
else
{
LerpHeadBySpeed( vec_zero , false );
return;
}
}
if (act->GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) && act->GetActorFlag(ACTOR_FLAG_USING_HUD) )
{
LerpHeadBySpeed( vec_zero , false );
return;
}
// Check if our _watchTarget is within distance )
if ( _maxDistance > 0 )
{
Vector selfToTarget = _watchTarget->origin - act->origin;
float dist = selfToTarget.length();
if ( dist > _maxDistance )
{
LerpHeadBySpeed( vec_zero , false );
return;
}
}
if ( _watchTarget->isSubclassOf( Actor ) )
{
actTarget = (Actor *)(Entity *)_watchTarget;
// Don't watch if the target is dead.
if ( !actTarget->isThinkOn() )
{
_watchTarget = NULL;
actTarget = NULL;
return;
}
}
if ( actTarget && ( actTarget->watch_offset != vec_zero ) )
{
MatrixTransformVector( actTarget->watch_offset, _watchTarget->orientation, watchPosition );
watchPosition += _watchTarget->origin;
}
else
{
tagNum = gi.Tag_NumForName( _watchTarget->edict->s.modelindex, "Bip01 Head" );
if ( tagNum < 0 )
watchPosition = _watchTarget->centroid;
else
{
_watchTarget->GetTag( "Bip01 Head", &watchPosition );
}
}
AdjustHeadAngles( tagPos , watchPosition );
}
void HeadWatcher::AdjustHeadAngles( const Vector &tagPos , const Vector &watchPosition )
{
Vector dir;
Vector angles;
Vector anglesDiff;
float yawChange;
float pitchChange;
dir = watchPosition - tagPos;
angles = dir.toAngles();
anglesDiff = angles - act->angles;
anglesDiff[YAW] = AngleNormalize180( anglesDiff[YAW] );
anglesDiff[PITCH] = AngleNormalize180( anglesDiff[PITCH] );
yawChange = anglesDiff[YAW];
pitchChange = anglesDiff[PITCH];
if ( _turnThreshold && ( yawChange < _turnThreshold ) && ( yawChange > -_turnThreshold ) && ( pitchChange < _turnThreshold ) && ( pitchChange > -_turnThreshold ) )
{
return;
}
// Make sure we don't turn neck too far
if ( anglesDiff[YAW] < -_maxHeadYaw )
anglesDiff[YAW] = -_maxHeadYaw;
else if ( anglesDiff[YAW] > _maxHeadYaw )
anglesDiff[YAW] = _maxHeadYaw;
if ( anglesDiff[PITCH] < -_maxHeadPitch )
anglesDiff[PITCH] = -_maxHeadPitch;
else if ( anglesDiff[PITCH] > _maxHeadPitch )
anglesDiff[PITCH] = _maxHeadPitch;
anglesDiff[ROLL] = 0.0f;
LerpHeadBySpeed( anglesDiff );
}
void HeadWatcher::LerpHeadBySpeed( const Vector &angleDelta , bool useTorsoAngles )
{
Vector anglesDiff;
Vector change;
Vector finalAngles;
Vector currentTorsoAngles;
anglesDiff = angleDelta;
// Get our Torso Angles
act->SetControllerTag( ACTOR_TORSO_TAG , gi.Tag_NumForName( act->edict->s.modelindex, "Bip01 Spine1" ) );
currentTorsoAngles = act->GetControllerAngles( ACTOR_TORSO_TAG );
//Reset our Controller Tag
act->SetControllerTag( ACTOR_HEAD_TAG, gi.Tag_NumForName( act->edict->s.modelindex, "Bip01 Head" ) );
// Make sure we don't change our head angles too much at once
change = anglesDiff - _currentHeadAngles;
if ( change[YAW] > _maxHeadTurnSpeed )
anglesDiff[YAW] = _currentHeadAngles[YAW] + _maxHeadTurnSpeed;
else if ( change[YAW] < -_maxHeadTurnSpeed )
anglesDiff[YAW] = _currentHeadAngles[YAW] - _maxHeadTurnSpeed;
if ( change[PITCH] > _maxHeadTurnSpeed )
anglesDiff[PITCH] = _currentHeadAngles[PITCH] + _maxHeadTurnSpeed;
else if ( change[PITCH] < -_maxHeadTurnSpeed )
anglesDiff[PITCH] = _currentHeadAngles[PITCH] - _maxHeadTurnSpeed;
if ( change[ROLL] > _maxHeadTurnSpeed )
anglesDiff[ROLL] = _currentHeadAngles[ROLL] + _maxHeadTurnSpeed;
else if ( change[ROLL] < -_maxHeadTurnSpeed )
anglesDiff[ROLL] = _currentHeadAngles[ROLL] - _maxHeadTurnSpeed;
finalAngles = anglesDiff;
if ( useTorsoAngles )
finalAngles[YAW] = anglesDiff[YAW] - currentTorsoAngles[YAW];
else
finalAngles[YAW] = anglesDiff[YAW];
act->SetControllerAngles( ACTOR_HEAD_TAG, finalAngles );
act->real_head_pitch = anglesDiff[PITCH];
_currentHeadAngles = anglesDiff;
}
void HeadWatcher::setHeadTwitch( bool twitchHead )
{
_twitchHead = twitchHead;
if ( _twitchHead )
{
_nextTwitchHeadTime = 0.0f;
}
}
void HeadWatcher::twitchHead( void )
{
Vector headAngles;
float oldMaxHeadTurnSpeed;
if ( level.time > _nextTwitchHeadTime )
{
_headTwitchAngles = Vector( G_CRandom( 4.0f ), G_CRandom( 4.0f ), G_CRandom( 4.0f ) );
_nextTwitchHeadTime = level.time + 1.0f + G_CRandom( 0.5f );
}
oldMaxHeadTurnSpeed = _maxHeadTurnSpeed;
_maxHeadTurnSpeed = 0.25f;
LerpHeadBySpeed( _headTwitchAngles, false );
_maxHeadTurnSpeed = oldMaxHeadTurnSpeed;
}
//
// Name: DoArchive()
// Parameters: Archiver &arc
// Actor *actor
// Description: Sets the Actor Pointer and Calls Archive()
//
void HeadWatcher::DoArchive( Archiver &arc , Actor *actor )
{
Archive( arc );
if ( actor )
act = actor;
else
gi.Error( ERR_FATAL, "HeadWatcher::DoArchive -- actor is NULL" );
}
//
// Name: Archive()
// Parameters: Archiver &arc
// Description: Archives Class Data
//
void HeadWatcher::Archive( Archiver &arc )
{
arc.ArchiveSafePointer( &_watchTarget );
arc.ArchiveVector( &_currentHeadAngles );
arc.ArchiveFloat( &_maxHeadTurnSpeed );
arc.ArchiveFloat( &_turnThreshold );
arc.ArchiveBoolean( &_explicitSet );
arc.ArchiveFloat( &_maxHeadYaw );
arc.ArchiveFloat( &_maxHeadPitch );
arc.ArchiveBool( &_twitchHead );
arc.ArchiveFloat( &_nextTwitchHeadTime );
arc.ArchiveVector( &_headTwitchAngles );
arc.ArchiveFloat( &_maxDistance );
arc.ArchiveBool( &_ignoreWatchTarget );
}