The-Simpsons-Hit-and-Run/game/code/ai/actor/attractionbehaviour.cpp

278 lines
8.6 KiB
C++

//===========================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// Component: AttractionBehaviour
//
// Description: This behaviour has two states. In the first state, it will
// cause the actor to remain motionless until something of interest
// happens. Then this happens, the actor will move down to near the avatar
// and watch him until the avatar does something violent
//
//
// Authors: Michael Riegger
//
//===========================================================================
//---------------------------------------------------------------------------
// Includes
//===========================================================================
#include <ai/actor/attractionbehaviour.h>
#include <ai/actor/actordsg.h>
#include <ai/actor/actor.h>
#include <ai/actor/flyingactor.h>
#include <events/eventmanager.h>
#include <worldsim/avatarmanager.h>
#include <memory/srrmemory.h>
#include <render/IntersectManager/IntersectManager.h>
//===========================================================================
// Local Constants, Typedefs, and Macros
//===========================================================================
//===========================================================================
// Global Data, Local Data, Local Classes
//===========================================================================
//===========================================================================
// Member Functions
//===========================================================================
AttractionBehaviour::AttractionBehaviour( float minWatchDistance, float maxWatchDistance, float speedKPH ):
m_CurrentState( eIdle ),
m_SensoryRangeSqr( maxWatchDistance * maxWatchDistance ),
m_MinWatchDistanceSqr( minWatchDistance * minWatchDistance ),
m_MaxWatchDistanceSqr( maxWatchDistance * maxWatchDistance ),
m_ForceFindNewWatchPosition( false ),
m_ParentActor( NULL )
{
m_Speed = speedKPH * 1000.0f / 3600000.0f;
m_WatchDistanceFromAvatar = ( minWatchDistance + maxWatchDistance ) / 2.0f;
HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
SetExclusive( true );
GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
GetEventManager()->AddListener( this, EVENT_OBJECT_DESTROYED );
GetEventManager()->AddListener( this, EVENT_BIG_CRASH );
GetEventManager()->AddListener( this, EVENT_BIG_VEHICLE_CRASH );
GetEventManager()->AddListener( this, EVENT_BIG_AIR );
GetEventManager()->AddListener( this, EVENT_PEDESTRIAN_DODGE );
GetEventManager()->AddListener( this, EVENT_PEDESTRIAN_SMACKDOWN );
GetEventManager()->AddListener( this, EVENT_BREAK_CAMERA_OR_BOX );
GetEventManager()->AddListener( this, EVENT_GAG_END );
GetEventManager()->AddListener( this, EVENT_PC_NPC_COLLISION );
GetEventManager()->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC );
GetEventManager()->AddListener( this, EVENT_HIT_BREAKABLE );
GetEventManager()->AddListener( this, EVENT_OBJECT_KICKED );
GetEventManager()->AddListener( this, EVENT_JUMP_LANDING );
GetEventManager()->AddListener( this, EVENT_KICK );
GetEventManager()->AddListener( this, EVENT_STOMP );
GetEventManager()->AddListener( this, EVENT_DOUBLEJUMP );
GetEventManager()->AddListener( this, EVENT_COLLECT_OBJECT );
HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
}
AttractionBehaviour::~AttractionBehaviour()
{
GetEventManager()->RemoveAll( this );
}
void
AttractionBehaviour::Apply( Actor* actor, unsigned int timeInMS )
{
m_ParentActor = actor;
// Make the actor stay fixed
FlyingActor* flyingactor = static_cast< FlyingActor* >( actor );
flyingactor->SetDesiredHeightEnabled( false );
Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer(0);
rAssert( avatar != NULL );
rmt::Vector avatarPosition;
avatar->GetPosition( avatarPosition );
rmt::Vector actorPosition;
actor->GetPosition( &actorPosition );
if ( m_ForceFindNewWatchPosition )
{
rmt::Vector newWatchPosition;
if ( FindNewWatchPosition( *m_ParentActor, avatarPosition, &newWatchPosition ) )
{
m_ParentActor->MoveTo( newWatchPosition, m_Speed );
m_ForceFindNewWatchPosition = false;
}
}
float distanceToTargetSqr = (actorPosition - avatarPosition).MagnitudeSqr();
const float waspRadius = 5.0f;
if( distanceToTargetSqr < waspRadius * waspRadius )
{
//
// Send a message about approaching a wasp
//
GetEventManager()->TriggerEvent( EVENT_WASP_APPROACHED );
}
if ( m_CurrentState == eIdle )
{
if ( distanceToTargetSqr < m_MinWatchDistanceSqr )
{
// The target is too close!
// Attraction go into watch mode
// SetExclusive( false );
}
}
else if ( m_CurrentState == eWatching )
{
// Basically, we want the actor to watch the avatar, follow him around while
// keeping his distance
// Lets first check the distance to the avatar
if ( distanceToTargetSqr < m_MinWatchDistanceSqr )
{
// The target is too close!
// Attraction behaviour is over, lets bail
SetExclusive( false );
}
else if ( IsMovementDisabled() == false && distanceToTargetSqr > m_MaxWatchDistanceSqr )
{
// The target is too far!
// Lets move close
rmt::Vector newWatchPosition;
if ( FindNewWatchPosition( *actor, avatarPosition, &newWatchPosition ) )
{
actor->MoveTo( newWatchPosition, m_Speed );
}
}
else
{
actor->LookAt( avatarPosition, timeInMS );
}
}
}
void
AttractionBehaviour::HandleEvent( EventEnum id, void* pEventData )
{
switch ( id )
{
case EVENT_COLLECT_OBJECT:
{
// Hostile action was taken
if( m_CurrentState == eWatching )
{
SetExclusive( false );
}
}
case EVENT_KICK:
case EVENT_STOMP:
case EVENT_DOUBLEJUMP:
case EVENT_VEHICLE_DESTROYED:
case EVENT_OBJECT_DESTROYED:
case EVENT_BIG_CRASH:
case EVENT_BIG_VEHICLE_CRASH:
case EVENT_BIG_AIR:
case EVENT_PEDESTRIAN_DODGE:
case EVENT_PEDESTRIAN_SMACKDOWN:
case EVENT_BREAK_CAMERA_OR_BOX:
case EVENT_GAG_END:
case EVENT_PC_NPC_COLLISION:
case EVENT_PLAYER_CAR_HIT_NPC:
case EVENT_HIT_BREAKABLE:
case EVENT_OBJECT_KICKED:
case EVENT_JUMP_LANDING:
{
if ( m_ParentActor != NULL )
{
Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer(0);
if ( WithinSensoryRange( *m_ParentActor, *avatar ) )
{
switch (m_CurrentState)
{
case eIdle:
{
// Make the object fly down to the actor
m_CurrentState = eWatching;
m_ForceFindNewWatchPosition = true;
}
break;
default:
break;
};
}
}
}
break;
default:
rAssert( false );
break;
};
}
void AttractionBehaviour::Activate()
{
m_CurrentState = eWatching;
SetExclusive( true );
}
void AttractionBehaviour::Deactivate()
{
m_CurrentState = eIdle;
m_ForceFindNewWatchPosition = false;
SetExclusive( false );
if ( m_ParentActor != NULL )
{
FlyingActor* flyingactor = static_cast< FlyingActor* >( m_ParentActor );
}
}
bool
AttractionBehaviour::WithinSensoryRange( const Actor& actor, const Avatar& avatar )const
{
bool withinRange;
rmt::Vector actorPosition;
actor.GetPosition( &actorPosition );
rmt::Vector avatarPosition;
avatar.GetPosition( avatarPosition );
if ( (avatarPosition - actorPosition).MagnitudeSqr() < m_SensoryRangeSqr )
{
withinRange = true;
}
else
{
withinRange = false;
}
return withinRange;
}
bool
AttractionBehaviour::FindNewWatchPosition( const Actor& actor, const rmt::Vector& avatarPosition, rmt::Vector* newPosition )const
{
// We want to find a new position halfway between min and max distance
// along the line of sight
rmt::Vector actorPosition;
actor.GetPosition( &actorPosition );
rmt::Vector toAvatar = avatarPosition - actorPosition;
toAvatar.Normalize();
*newPosition = actorPosition + toAvatar * m_WatchDistanceFromAvatar;
return true;
}