328 lines
11 KiB
C++
328 lines
11 KiB
C++
//===========================================================================
|
|
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
|
|
//
|
|
// Component: projectile
|
|
//
|
|
// Description: a weapon actor
|
|
//
|
|
// Authors: Michael Riegger
|
|
//
|
|
//===========================================================================
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Includes
|
|
//===========================================================================
|
|
|
|
#include <ai/actor/projectile.h>
|
|
#include <stateprop/statepropdata.hpp>
|
|
#include <render/rendermanager/rendermanager.h>
|
|
#include <render/rendermanager/worldrenderlayer.h>
|
|
#include <atc/atcmanager.h>
|
|
#include <ai/actor/projectiledsg.h>
|
|
#include <render/IntersectManager/IntersectManager.h>
|
|
#include <worldsim/character/character.h>
|
|
#include <worldsim/coins/coinmanager.h>
|
|
#include <worldsim/redbrick/vehicle.h>
|
|
|
|
|
|
//===========================================================================
|
|
// Local Constants, Typedefs, and Macros
|
|
//===========================================================================
|
|
|
|
const float VEHICLE_DAMAGE_ON_PROJECTILE_HIT = 0.1f;
|
|
const int COINS_LOST_BY_PLAYER_ON_HIT = 5;
|
|
|
|
|
|
//===========================================================================
|
|
// Global Data, Local Data, Local Classes
|
|
//===========================================================================
|
|
|
|
float Projectile::s_Speed = 20.0f;
|
|
const float PROJECTILE_MAX_RANGE = 25.0f;
|
|
|
|
//===========================================================================
|
|
// Member Functions
|
|
//===========================================================================
|
|
|
|
Projectile::Projectile():
|
|
mpStateProp( NULL ),
|
|
m_WillHitStatic( false )
|
|
{
|
|
}
|
|
|
|
Projectile::~Projectile()
|
|
{
|
|
if ( mpStateProp )
|
|
{
|
|
mpStateProp->Release();
|
|
mpStateProp = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
Projectile::SetSpeed( float kph )
|
|
{
|
|
// convert to meters / millisecond
|
|
s_Speed = kph * 1000.0f / ( 3600.0f * 1000.0f );
|
|
}
|
|
|
|
bool Projectile::Init( const char* statePropName, const char* instanceName )
|
|
{
|
|
bool success;
|
|
|
|
CStatePropData* pStatePropData = p3d::find< CStatePropData > ( statePropName );
|
|
if ( pStatePropData )
|
|
{
|
|
mp_StateProp = new ProjectileDSG();
|
|
mp_StateProp->AddRef();
|
|
|
|
CollisionAttributes* pCollAttr = GetATCManager()->CreateCollisionAttributes( PROP_MOVEABLE, 1, 2.2425f );
|
|
rmt::Matrix transform;
|
|
transform.Identity();
|
|
mp_StateProp->LoadSetup( pStatePropData, 0, transform, pCollAttr, true, false );
|
|
mp_StateProp->SetName( instanceName );
|
|
mp_StateProp->EnableCollisionVolume( false );
|
|
|
|
success = true;
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
void
|
|
Projectile::Update( unsigned int timeInMS )
|
|
{
|
|
rmt::Matrix transform;
|
|
GetTransform( &transform );
|
|
m_CurrentPosition = transform.Row(3);
|
|
|
|
|
|
// Lets see if we would have hit anything
|
|
int currState = mp_StateProp->GetState();
|
|
if ( currState == 0 )
|
|
{
|
|
// Test dynamics
|
|
FillIntersectionListDynamics( m_CurrentPosition, 10.0f );
|
|
rmt::Vector dynamicsIntersectionPoint;
|
|
DynaPhysDSG* objectHit;
|
|
if ( m_IntersectionList.TestIntersectionDynamics( m_PreviousPosition, m_CurrentPosition, &dynamicsIntersectionPoint, &objectHit ) )
|
|
{
|
|
if ( IsJumpingPlayer( objectHit ) == false )
|
|
{
|
|
// we hit something, make it explode at the collisionpoint
|
|
m_Speed = 0;
|
|
rmt::Vector adjustedIntersection;
|
|
AdjustCollisionPosition( dynamicsIntersectionPoint, m_CurrentPosition, m_PreviousPosition, &adjustedIntersection );
|
|
SetPosition( adjustedIntersection );
|
|
mp_StateProp->SetState( 1 );
|
|
ApplyDamage( objectHit );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if ( m_WillHitStatic )
|
|
{
|
|
// Test to see if the projectile hit the static this frame
|
|
// Take the dot between (Intersection - Current) and ( Intersection - Previous )
|
|
// If negative, collision
|
|
rmt::Vector toPrev = m_PreviousPosition - m_StaticIntersectionPoint;
|
|
rmt::Vector toCurr = m_CurrentPosition - m_StaticIntersectionPoint;
|
|
if ( toCurr.Dot( toPrev ) < 0 )
|
|
{
|
|
// we hit something, make it explode at the collisionpoint
|
|
m_Speed = 0;
|
|
SetPosition( m_StaticIntersectionPoint );
|
|
rmt::Vector adjustedIntersection;
|
|
AdjustCollisionPosition( m_StaticIntersectionPoint, m_CurrentPosition, m_PreviousPosition, &adjustedIntersection );
|
|
SetPosition( adjustedIntersection );
|
|
mp_StateProp->SetState( 1 );
|
|
}
|
|
}
|
|
m_IntersectionList.ClearDynamics();
|
|
|
|
{
|
|
// Keep flying
|
|
rmt::Vector velocityVectory = transform.Row(2) * m_Speed;
|
|
rmt::Vector position;
|
|
GetPosition( &position );
|
|
position = position + velocityVectory * static_cast< float >( timeInMS );
|
|
SetPosition( position );
|
|
}
|
|
}
|
|
|
|
|
|
m_PreviousPosition = m_CurrentPosition;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Can't see a use for projectile behaviours at this point
|
|
void Projectile::AddBehaviour( Behaviour* )
|
|
{
|
|
|
|
}
|
|
|
|
void Projectile::Fire()
|
|
{
|
|
m_Speed = s_Speed;
|
|
}
|
|
|
|
|
|
void Projectile::CalculateIntersections()
|
|
{
|
|
|
|
// Calculate the static intersection
|
|
rmt::Matrix transform;
|
|
GetTransform( &transform );
|
|
const rmt::Vector& position = transform.Row(3);
|
|
|
|
// Lets query all objects around the projectile and see if it will (eventually)
|
|
// hit a static object
|
|
FillIntersectionListStatics( position, PROJECTILE_MAX_RANGE );
|
|
|
|
rmt::Vector destination;
|
|
destination = position + PROJECTILE_MAX_RANGE * transform.Row(2);
|
|
m_WillHitStatic = m_IntersectionList.TestIntersectionStatics( position, destination, &m_StaticIntersectionPoint );
|
|
|
|
// No use for the static list anymore, ditch it
|
|
m_IntersectionList.ClearStatics();
|
|
}
|
|
|
|
|
|
void Projectile::AdjustCollisionPosition( const rmt::Vector& intersection,
|
|
const rmt::Vector& currPos,
|
|
const rmt::Vector& prevPos,
|
|
rmt::Vector* newIntersection )
|
|
{
|
|
// ToIntersection
|
|
if( currPos != prevPos )
|
|
{
|
|
rmt::Vector toIntersection = currPos - prevPos;
|
|
toIntersection.Normalize();
|
|
*newIntersection = intersection + toIntersection * 0.75f;
|
|
}
|
|
else
|
|
{
|
|
*newIntersection = intersection;
|
|
}
|
|
}
|
|
|
|
void Projectile::ApplyDamage( DynaPhysDSG* object )
|
|
{
|
|
// Determine the type of the object and perform an action depending
|
|
// upon the AI ref
|
|
switch (object->GetAIRef())
|
|
{
|
|
case PhysicsAIRef::redBrickVehicle:
|
|
{
|
|
Vehicle* v = (Vehicle*)object;
|
|
if ( v->mVehicleType == VT_TRAFFIC )
|
|
{
|
|
v->TriggerDamage( VEHICLE_DAMAGE_ON_PROJECTILE_HIT );
|
|
}
|
|
break;
|
|
}
|
|
case PhysicsAIRef::PlayerCharacter:
|
|
{
|
|
// Make the character get shocked
|
|
rAssert( dynamic_cast< Character* >( static_cast< tRefCounted* >( object ) ) );
|
|
Character* character = static_cast< Character* >( object );
|
|
rmt::Vector characterPosition;
|
|
character->GetPosition( &characterPosition );
|
|
GetCoinManager()->LoseCoins( COINS_LOST_BY_PLAYER_ON_HIT , &characterPosition );
|
|
character->Shock( 0.5f );
|
|
break;
|
|
}
|
|
case PhysicsAIRef::NPCharacter:
|
|
{
|
|
// Make the character get shocked
|
|
rAssert( dynamic_cast< Character* >( static_cast< tRefCounted* >( object ) ) );
|
|
Character* character = static_cast< Character* >( object );
|
|
|
|
// Lets try a little velocity change to get the peds moving a bit after they get
|
|
// shot
|
|
sim::SimState* pSimState = character->GetSimState();
|
|
// Modifying the velocity will be done using a non-const reference to
|
|
// the velocity vector
|
|
rmt::Vector& rVelocity = pSimState->GetLinearVelocity();
|
|
// Apply delta velocity
|
|
// Lets play with the direction the NPC will fly to
|
|
|
|
rmt::Vector direction( 0, 0.707f, 0.707f );
|
|
rmt::Matrix transform;
|
|
GetTransform( &transform );
|
|
transform.RotateVector( direction, &direction );
|
|
|
|
float deltaV = 10.0f;
|
|
rVelocity += (direction * deltaV);
|
|
// Make it interact with the world
|
|
character->AddToSimulation();
|
|
|
|
break;
|
|
}
|
|
case PhysicsAIRef::StateProp:
|
|
{
|
|
// Earl wants these things to ignore vending machines and crates
|
|
const char* ignore_list[] =
|
|
{
|
|
"l1_crate", "l2_crate", "l3_crate", "l4_crate", "l5_crate", "l6_crate", "l7_crate",
|
|
"l1_vending", "l2_vending", "l3_vending", "l4_vending", "l5_vending", "l6_vending", "l7_vending"
|
|
};
|
|
const int num_items_to_ignore = sizeof( ignore_list ) / sizeof( ignore_list[0] );
|
|
|
|
StatePropDSG* spdsg = static_cast< StatePropDSG* >( object );
|
|
tUID uid = spdsg->GetStatePropUID();
|
|
|
|
|
|
bool isSpecialObject = false;
|
|
for ( int i = 0 ; i < num_items_to_ignore ; i++ )
|
|
{
|
|
if( tName::MakeUID( ignore_list[i] ) == uid )
|
|
{
|
|
// A special object, do nothing
|
|
isSpecialObject = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( isSpecialObject == false )
|
|
{
|
|
rmt::Vector direction( 0, 0.707f, 0.707f );
|
|
rmt::Matrix transform;
|
|
GetTransform( &transform );
|
|
transform.RotateVector( direction, &direction );
|
|
object->ApplyForce( direction, 400.0f );
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
rmt::Vector direction( 0, 0.707f, 0.707f );
|
|
rmt::Matrix transform;
|
|
GetTransform( &transform );
|
|
transform.RotateVector( direction, &direction );
|
|
object->ApplyForce( direction, 400.0f );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Projectile::IsJumpingPlayer( DynaPhysDSG* object )
|
|
{
|
|
bool jumpingCharacter = false;
|
|
if ( object->GetAIRef() == PhysicsAIRef::PlayerCharacter )
|
|
{
|
|
Character* character = static_cast< Character* >( object );
|
|
if ( character->IsJumping() )
|
|
jumpingCharacter = true;
|
|
}
|
|
return jumpingCharacter;
|
|
}
|
|
|
|
|