mirror of
https://github.com/thug1src/thug.git
synced 2024-11-30 12:06:44 +00:00
2732 lines
94 KiB
C++
2732 lines
94 KiB
C++
//****************************************************************************
|
|
//* MODULE: Gel/Components
|
|
//* FILENAME: MotionComponent.cpp
|
|
//* OWNER: Gary Jesdanun
|
|
//* CREATION DATE: 11/5/2002
|
|
//****************************************************************************
|
|
|
|
// start autoduck documentation
|
|
// @DOC motioncomponent
|
|
// @module motioncomponent | None
|
|
// @subindex Scripting Database
|
|
// @index script | motioncomponent
|
|
|
|
#include <gel/components/motioncomponent.h>
|
|
#include <gel/components/pedlogiccomponent.h>
|
|
#include <gel/components/lockobjcomponent.h>
|
|
#include <gel/components/modelcomponent.h>
|
|
|
|
#include <core/math/slerp.h>
|
|
|
|
#include <gel/collision/collision.h>
|
|
#include <gel/object/compositeobject.h>
|
|
#include <gel/scripting/script.h>
|
|
#include <gel/scripting/struct.h>
|
|
|
|
#include <sk/engine/feeler.h>
|
|
#include <sk/objects/pathob.h>
|
|
#include <sk/objects/pathman.h>
|
|
#include <sk/scripting/nodearray.h>
|
|
#include <sk/objects/followob.h>
|
|
|
|
|
|
|
|
namespace Obj
|
|
{
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
enum
|
|
{
|
|
UNITS_IPS,
|
|
UNITS_FPS,
|
|
UNITS_MPH,
|
|
};
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// This static function is what is registered with the component factory
|
|
// object, (currently the CCompositeObjectManager)
|
|
CBaseComponent* CMotionComponent::s_create()
|
|
{
|
|
return static_cast< CBaseComponent* >( new CMotionComponent );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CMotionComponent::CMotionComponent() : CBaseComponent()
|
|
{
|
|
SetType( CRC_MOTION );
|
|
|
|
mp_pathOb = NULL;
|
|
|
|
mp_path_object_tracker = NULL;
|
|
|
|
m_point_stick_to_ground = true;
|
|
|
|
Reset();
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CMotionComponent::~CMotionComponent()
|
|
{
|
|
if ( mp_pathOb )
|
|
{
|
|
delete mp_pathOb;
|
|
mp_pathOb = NULL;
|
|
}
|
|
|
|
// Note: No need to unregister from mp_path_object_tracker, because the path tracker
|
|
// uses smart pointers.
|
|
|
|
if ( mp_slerp )
|
|
{
|
|
delete mp_slerp;
|
|
mp_slerp = NULL;
|
|
}
|
|
|
|
if (mp_follow_ob)
|
|
{
|
|
delete mp_follow_ob;
|
|
mp_follow_ob=NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::InitFromStructure( Script::CStruct* pParams )
|
|
{
|
|
int nodeNum = -1;
|
|
pParams->GetInteger( CRCD(0xe50d6573,"nodeIndex"), &nodeNum, Script::NO_ASSERT );
|
|
m_start_node = m_current_node = nodeNum;
|
|
OrientToNode(pParams);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CMotionComponent::ProcessWait( Script::CScript * pScript )
|
|
{
|
|
switch (pScript->GetWaitType())
|
|
{
|
|
case Script::WAIT_TYPE_OBJECT_MOVE:
|
|
if ( !IsMoving() )
|
|
{
|
|
pScript->ClearWait();
|
|
}
|
|
break;
|
|
|
|
case Script::WAIT_TYPE_OBJECT_JUMP_FINISHED:
|
|
if ( !(m_movingobj_status & MOVINGOBJ_STATUS_JUMPING) )
|
|
{
|
|
pScript->ClearWait();
|
|
}
|
|
break;
|
|
|
|
case Script::WAIT_TYPE_OBJECT_ROTATE:
|
|
if ( !IsRotating() )
|
|
{
|
|
pScript->ClearWait();
|
|
}
|
|
break;
|
|
case Script::WAIT_TYPE_OBJECT_STOP_FINISHED:
|
|
if ( !( m_vel_z > 0.0f ) )
|
|
{
|
|
pScript->ClearWait();
|
|
}
|
|
break;
|
|
default:
|
|
Dbg_MsgAssert(0,("\n%s\nWait type of %d not supported by CMotionComponent",pScript->GetScriptInfo(),pScript->GetWaitType()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::Update()
|
|
{
|
|
|
|
m_time = Tmr::FrameLength();
|
|
|
|
if ( m_movingobj_status & MOVINGOBJ_STATUS_HOVERING )
|
|
{
|
|
// Having these functions next to each other here does mean that objects cannot be
|
|
// moving and hovering at the same time, but currently nothing requires that.
|
|
UndoHover();
|
|
ApplyHover();
|
|
}
|
|
|
|
if ( m_movingobj_status & MOVINGOBJ_STATUS_ROTXYZ )
|
|
{
|
|
RotUpdate( );
|
|
}
|
|
|
|
if ( m_movingobj_status & MOVINGOBJ_STATUS_QUAT_ROT )
|
|
{
|
|
QuatRot( );
|
|
}
|
|
|
|
if ( m_movingobj_status & MOVINGOBJ_STATUS_MOVETO )
|
|
{
|
|
if ( Move( ) )
|
|
{
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
|
|
}
|
|
}
|
|
else if ( m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH )
|
|
{
|
|
FollowPath( );
|
|
}
|
|
else if ( m_movingobj_status & MOVINGOBJ_STATUS_FOLLOWING_LEADER )
|
|
{
|
|
Dbg_MsgAssert( !(m_movingobj_status & MOVINGOBJ_STATUS_QUAT_ROT), ("Cannot have object rotating when it is following a leader"));
|
|
Dbg_MsgAssert( !(m_movingobj_status & MOVINGOBJ_STATUS_ROTXYZ), ("Cannot have object rotating when it is following a leader"));
|
|
FollowLeader( );
|
|
}
|
|
|
|
if ( m_movingobj_status & MOVINGOBJ_STATUS_JUMPING )
|
|
{
|
|
DoJump();
|
|
GetObject()->m_pos = m_jump_pos;
|
|
// m_pos[Y]=m_jump_y;
|
|
}
|
|
|
|
|
|
// TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
|
|
// BIT OF A PATCH, as the skater has a motion component
|
|
// but we don't want to set his display matrix directly here.
|
|
if (GetObject()->GetID() > 15)
|
|
{
|
|
GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
|
|
}
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CMotionComponent::IsMoving()
|
|
{
|
|
return ( m_movingobj_status & ( MOVINGOBJ_STATUS_ON_PATH | MOVINGOBJ_STATUS_MOVETO ) );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CMotionComponent::IsRotating()
|
|
{
|
|
return ( m_movingobj_status & ( MOVINGOBJ_STATUS_ROTXYZ | MOVINGOBJ_STATUS_QUAT_ROT ) );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
static void s_get_velocity( Script::CStruct* pParams, float* pVel )
|
|
{
|
|
if ( pParams->GetFloat( NONAME, pVel ) )
|
|
{
|
|
// default is mph:
|
|
if ( pParams->ContainsFlag( 0xaad7c80f ) ) // 'fps'
|
|
{
|
|
*pVel = FEET_TO_INCHES( *pVel );
|
|
}
|
|
else if ( !( pParams->ContainsFlag( 0xa18b8f32 ) ) ) // 'ips'
|
|
{
|
|
// miles per hour then...
|
|
*pVel = MPH_TO_IPS( *pVel );
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
static void s_get_acceleration( Script::CStruct* pParams, float* pAccel )
|
|
{
|
|
if ( pParams->GetFloat( NONAME, pAccel ) )
|
|
{
|
|
// default is mphps:
|
|
if ( pParams->ContainsFlag( 0xf414a5ea ) ) // 'fpsps'
|
|
{
|
|
*pAccel = FEET_TO_INCHES( *pAccel );
|
|
}
|
|
else if ( !( pParams->ContainsFlag( 0x7644323b ) ) ) // 'ipsps'
|
|
{
|
|
// miles per hour per second then...
|
|
*pAccel = MPH_TO_IPS( *pAccel );
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::UndoHover( void )
|
|
{
|
|
Mth::Vector pos=GetObject()->GetPos();
|
|
pos[Y]=m_y_before_applying_hover;
|
|
GetObject()->SetPos(pos);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::ApplyHover( void )
|
|
{
|
|
uint32 t=Tmr::ElapsedTime(0)%m_hover_period;
|
|
m_hover_offset=m_hover_amp*sinf(t*2*3.141592653f/m_hover_period);
|
|
Mth::Vector pos=GetObject()->GetPos();
|
|
m_y_before_applying_hover=pos[Y];
|
|
pos[Y]+=m_hover_offset;
|
|
GetObject()->SetPos(pos);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CMotionComponent::IsHovering( void )
|
|
{
|
|
return (m_movingobj_status & MOVINGOBJ_STATUS_HOVERING) &&
|
|
// If it is doing a Quat rot, then pretend it is not hovering.
|
|
// This is so that the rocking buoy in the shipyard is not indicated as
|
|
// being hovering, otherwise the replay code will show it as just hovering
|
|
// up and down. The replay code optimizes hovering objects by not recording their
|
|
// precise positions and orientations every frame. We need that for the buoy though.
|
|
!(m_movingobj_status & (MOVINGOBJ_STATUS_QUAT_ROT));
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::GetHoverOrgPos( Mth::Vector* p_orgPos )
|
|
{
|
|
Dbg_MsgAssert(p_orgPos,("NULL p_orgPos"));
|
|
Mth::Vector pos=GetObject()->GetPos();
|
|
p_orgPos->Set(pos[X],m_y_before_applying_hover,pos[Z]);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CBaseComponent::EMemberFunctionResult CMotionComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
|
|
{
|
|
switch ( Checksum )
|
|
{
|
|
// @script | Obj_IsMoving | true if moving
|
|
case 0x21accf0d: // Obj_IsMoving
|
|
return ( IsMoving() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE );
|
|
break;
|
|
|
|
// @script | Obj_IsRotating | true if rotating
|
|
case 0x7bc461ae: // Obj_IsRotating
|
|
return ( IsRotating() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE );
|
|
break;
|
|
|
|
// @script | Obj_StorePos | stores the current position
|
|
case 0x8e52abea: // Obj_StorePos
|
|
m_stored_pos = GetObject()->GetPos();
|
|
break;
|
|
|
|
// @script | Obj_StoreNode | stores the next waypoint in the path
|
|
case 0xc8cc6820: // Obj_StoreNode
|
|
Dbg_MsgAssert( GetPathOb(),( "\n%s\nObject trying to store waypoint when not on a path.", pScript->GetScriptInfo( ) ));
|
|
m_stored_node = GetPathOb()->m_node_to;
|
|
break;
|
|
|
|
// @script | Obj_StopMoving |
|
|
case 0x68e1f022: // Obj_StopMoving
|
|
m_movingobj_status &= ~( MOVINGOBJ_STATUS_MOVETO | MOVINGOBJ_STATUS_ON_PATH );
|
|
break;
|
|
|
|
// @script | Obj_StopRotating |
|
|
case 0xdfbe3db3: // Obj_StopRotating
|
|
m_movingobj_status &= ~( MOVINGOBJ_STATUS_ROTX | MOVINGOBJ_STATUS_ROTY |
|
|
MOVINGOBJ_STATUS_ROTZ | MOVINGOBJ_STATUS_QUAT_ROT | MOVINGOBJ_STATUS_LOOKAT );
|
|
break;
|
|
|
|
// @script | Obj_SetPathVelocity |
|
|
// @uparm 1.0 | velocity
|
|
// @flag mph | default units
|
|
// @flag ips |
|
|
// @flag fps |
|
|
case 0xfb603ea9: // Obj_SetPathVelocity
|
|
s_get_velocity( pParams, &m_max_vel );
|
|
break;
|
|
|
|
// @script | Obj_SetPathMinStopVel | Speed at which friction
|
|
// overcomes momentum and the object stops. If left at zero,
|
|
// the object will take forever to stop... A good value for
|
|
// a car is 1 or 2 mph. Pedestrians are more 'sticky': make
|
|
// it 3 or 4 mph for them. Only used if you call Obj_StopAlongPath
|
|
// @uparm 1.0 | velocity
|
|
// @flag mph | default units
|
|
// @flag ips |
|
|
// @flag fps |
|
|
case 0x2ca63cdf: // Obj_SetPathMinStopVel
|
|
s_get_velocity( pParams, &m_min_stop_vel );
|
|
break;
|
|
|
|
// @script | Obj_SetPathAcceleration | used for when the object
|
|
// is stopped, and needs to get up to speed (set by Obj_SetVelocity).
|
|
// @uparm 1.0 | acceleration
|
|
// @flag mphps | default units
|
|
// @flag ipsps |
|
|
// @flag fpsps |
|
|
case 0xd6697ad0: // Obj_SetPathAcceleration
|
|
s_get_acceleration( pParams, &m_acceleration );
|
|
break;
|
|
|
|
// @script | Obj_SetPathDeceleration | used if the velocity is
|
|
// decreased while the object is traveling, to slow down to the new speed
|
|
// @uparm 1.0 | acceleration
|
|
// @flag mphps | default units
|
|
// @flag ipsps |
|
|
// @flag fpsps |
|
|
case 0xa67fc783: // Obj_SetPathDeceleration
|
|
s_get_acceleration( pParams, &m_deceleration );
|
|
break;
|
|
|
|
// @script | Obj_Rotate | rotate object
|
|
// @parmopt float | time | 0.0 | in seconds - if not included, instant rotation
|
|
// @parmopt vector | relative | (0, 0, 0) | rotate relative to the object
|
|
// @parmopt vector | absolute | (0, 0, 0) | rotate oriented along world axes
|
|
// @flag FLAG_MAX_COORDS | with this flag, coordinates will be converted to
|
|
// correspond to the axes in Max ( Z up, -Y forward, and X right. )
|
|
case 0x5cf1b7d4: // "Obj_Rotate"
|
|
QuatRot_Init( pParams );
|
|
break;
|
|
|
|
// @script | Obj_RotX | rotate about x axis
|
|
// @parmopt float | angle | | If not specified, object will rotate forever
|
|
// @parmopt float | speed | | Speed of rotation in degrees per second. <nl>
|
|
// Required unless deltaROT is ZERO
|
|
// @parmopt float | acceleration | | Acceleration in degrees per
|
|
// second per second. If not set, velocity is set instantly
|
|
// @parmopt float | deceleration | | If specified, velocity is set instantly.
|
|
// Deceleration in degrees per
|
|
// second per second. Target stops rotating when it reaches the target
|
|
// angle specified, or when speed reaches ZERO
|
|
// @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to
|
|
// correspond to the axes in Max ( Z up, -Y forward, and X right. )
|
|
case 0x0b231a9d: // "Obj_RotX"
|
|
RotAxis_Init( pParams, pScript, X );
|
|
break;
|
|
|
|
// @script | Obj_RotY | rotate about Y axis
|
|
// @parmopt float | angle | | If not specified, object will rotate forever
|
|
// @parmopt float | speed | | Speed of rotation in degrees per second. <nl>
|
|
// Required unless deltaROT is ZERO
|
|
// @parmopt float | acceleration | | Acceleration in degrees per
|
|
// second per second. If not set, velocity is set instantly
|
|
// @parmopt float | deceleration | | If specified, velocity is set instantly.
|
|
// Deceleration in degrees per
|
|
// second per second. Target stops rotating when it reaches the target
|
|
// angle specified, or when speed reaches ZERO
|
|
// @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to
|
|
// correspond to the axes in Max ( Z up, -Y forward, and X right. )
|
|
case 0x7c242a0b: // "Obj_RotY"
|
|
RotAxis_Init( pParams, pScript, Y );
|
|
break;
|
|
|
|
// @script | Obj_RotZ | rotate about z axis
|
|
// @parmopt float | angle | | If not specified, object will rotate forever
|
|
// @parmopt float | speed | | Speed of rotation in degrees per second. <nl>
|
|
// Required unless deltaROT is ZERO
|
|
// @parmopt float | acceleration | | Acceleration in degrees per
|
|
// second per second. If not set, velocity is set instantly
|
|
// @parmopt float | deceleration | | If specified, velocity is set instantly.
|
|
// Deceleration in degrees per
|
|
// second per second. Target stops rotating when it reaches the target
|
|
// angle specified, or when speed reaches ZERO
|
|
// @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to
|
|
// correspond to the axes in Max ( Z up, -Y forward, and X right. )
|
|
case 0xe52d7bb1: // "Obj_RotZ"
|
|
RotAxis_Init( pParams, pScript, Z );
|
|
break;
|
|
|
|
// @script | Obj_PathHeading | Defaults to on. Sets the
|
|
// heading of the object in the direction of the path
|
|
// @flag off | use to set off
|
|
case 0xd8f4bc32: // "Obj_PathHeading"
|
|
if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
|
|
{
|
|
m_movingobj_flags |= MOVINGOBJ_FLAG_INDEPENDENT_HEADING;
|
|
}
|
|
else
|
|
{
|
|
m_movingobj_flags &= ~MOVINGOBJ_FLAG_INDEPENDENT_HEADING;
|
|
}
|
|
break;
|
|
|
|
// @Script | Obj_SetConstantHeight | sets object height to whatever
|
|
// height the object is currently at
|
|
// @flag off | turn it off (default is on)
|
|
case 0xaa3cb476: // "Obj_SetConstantHeight"
|
|
if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
|
|
{
|
|
m_movingobj_flags &= ~MOVINGOBJ_FLAG_CONSTANT_HEIGHT;
|
|
}
|
|
else
|
|
{
|
|
m_movingobj_flags |= MOVINGOBJ_FLAG_CONSTANT_HEIGHT;
|
|
pParams->GetFloat( NONAME, &GetObject()->m_pos[ Y ] );
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_GetNextObjOnPath | Searches forward along the path the object is on for a max
|
|
// of Range feet, and if another object on the path is encountered, its name is put into a parameter
|
|
// called Ob. If no object is found, no Ob parameter will be created, and if it existed before, it
|
|
// will be removed.
|
|
// @parmopt float | Range | 0.0 | Distance to search, in feet
|
|
case 0xb75589ce: // Obj_GetNextObjOnPath
|
|
{
|
|
float range=100.0f;
|
|
pParams->GetFloat(CRCD(0x6c78a5b6,"Range"),&range);
|
|
range*=12.0f;
|
|
|
|
pScript->GetParams()->RemoveComponent( CRCD(0xffff9a1c,"Ob") );
|
|
|
|
CCompositeObject* p_closest_ob = GetNextObjectOnPath( range );
|
|
|
|
if ( p_closest_ob )
|
|
pScript->GetParams()->AddChecksum( CRCD(0xffff9a1c,"Ob"), p_closest_ob->GetID() );
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_GetRandomLink | Chooses a random node that the object's current node is
|
|
// linked to, and puts its link number into a parameter called Link.
|
|
// If the object's current node is not linked to anything, no Link parameter will be created.
|
|
case 0x6866312f: // Obj_GetRandomLink
|
|
{
|
|
uint32 num_links = SkateScript::GetNumLinks( m_current_node );
|
|
if (num_links)
|
|
{
|
|
// Note: The +1 is because we're using the convention of numbering the
|
|
// links from 1 to n rather than 0 to n-1
|
|
pScript->GetParams()->AddInteger(CRCD(0xc953660e,"Link"),Mth::Rnd(num_links)+1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_RandomPathMode | Causes Obj_FollowPath commands to
|
|
// pick random links as moving objects traverse paths
|
|
// @flag off | use to turn off - default is on
|
|
case 0x434ea1fb: // Obj_RandomPathMode
|
|
if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
|
|
{
|
|
m_movingobj_flags &= ~MOVINGOBJ_FLAG_RANDOM_PATH_MODE;
|
|
}
|
|
else
|
|
{
|
|
m_movingobj_flags |= MOVINGOBJ_FLAG_RANDOM_PATH_MODE;
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_SetPathTurnDist | The distance from a waypoint
|
|
// at which the object starts turning
|
|
// @uparm 1.0 | distance in feet
|
|
// @flag inches | use units of inches for distance
|
|
case 0x76003d82: // "Obj_SetPathTurnDist"
|
|
{
|
|
float enterTurnDist;
|
|
if ( pParams->GetFloat( NONAME, &enterTurnDist ) )
|
|
{
|
|
if ( !pParams->ContainsFlag( 0xf915b4f3 ) ) // "inches"
|
|
{
|
|
enterTurnDist = FEET_TO_INCHES( enterTurnDist );
|
|
}
|
|
EnsurePathobExists( GetObject() );
|
|
GetPathOb()->m_enter_turn_dist = enterTurnDist;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_FollowPath | Must also have Obj_SetVelocity set.
|
|
// If the node has no links, the object will move to the node and
|
|
// stop. Linked in a circle: object follows loop until stopped.
|
|
// Linked to a finite path: walks to the end of the path. Returns
|
|
// true for Obj_IsMoving until path complete. If used in conjunction
|
|
// with Obj_MoveTo functions, will turn moveto function off, and
|
|
// vice versa
|
|
// @parm name | Name | name of node at beginning of path
|
|
case 0xbfb0031d: // "Obj_FollowPath"
|
|
m_movingobj_flags &= ~MOVINGOBJ_FLAG_STOP_ALONG_PATH;
|
|
FollowPath_Init( pParams );
|
|
break;
|
|
|
|
// @script | Obj_LookAtNode |
|
|
// @parm name | Name | the name of the node you want to look at
|
|
// @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
|
|
// @parmopt float | time | vel/acceleration | in seconds
|
|
// @parmopt float | speed | 360 | angular velocity in degrees per second
|
|
// @parmopt float | acceleration | 0 | angular acceleration in degrees per second
|
|
// per second
|
|
// @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to
|
|
// correspond to the axes in Max ( Z up, -Y forward, and X right. )
|
|
case 0x103bd253: // "Obj_LookAtNode"
|
|
LookAtNode_Init( pParams, pScript );
|
|
break;
|
|
|
|
// @script | Obj_LookAtNodeLinked | looks at the next node
|
|
// @parmopt int | linkNum | 1 | The link to look at.
|
|
case 0x6419ceff: // "Obj_LookAtNodeLinked"
|
|
LookAtNodeLinked_Init( pParams, pScript );
|
|
break;
|
|
|
|
// @script | Obj_LookAtNodeStored | looks at stored node
|
|
// @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
|
|
// @parmopt float | time | vel/acceleration | in seconds
|
|
// @parmopt float | speed | 360 | angular velocity in degrees per second
|
|
// @parmopt float | acceleration | 0 | angular acceleration in degrees per second
|
|
// per second
|
|
// @parmopt float | AngleThreshold | 0 | If the object is already looking within this
|
|
// many degrees of the correct direction, it will not bother turning.
|
|
// @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to
|
|
// correspond to the axes in Max ( Z up, -Y forward, and X right. )
|
|
case 0xa5a2d218: // "Obj_LookAtNodeStored"
|
|
{
|
|
Mth::Vector lookAtPos;
|
|
SkateScript::GetPosition( m_stored_node, &lookAtPos );
|
|
LookAt_Init( pParams, lookAtPos );
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_LookAtPos |
|
|
// @uparm (0, 0, 0) | position vector
|
|
// @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
|
|
// @parmopt float | time | vel/acceleration | in seconds
|
|
// @parmopt float | speed | 360 | angular velocity in degrees per second
|
|
// @parmopt float | acceleration | 0 | angular acceleration in degrees per second
|
|
// per second
|
|
case 0x74c30242: // "Obj_LookAtPos"
|
|
LookAtPos_Init( pParams, true );
|
|
break;
|
|
|
|
// @script | Obj_LookAtPosStored | look at stored pos
|
|
// @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
|
|
// @parmopt float | time | vel/acceleration | in seconds
|
|
// @parmopt float | speed | 360 | angular velocity in degrees per second
|
|
// @parmopt float | acceleration | 0 | angular acceleration in degrees per second
|
|
// per second
|
|
// @parmopt float | AngleThreshold | 0 | If the object is already looking within this
|
|
// many degrees of the correct direction, it will not bother turning.
|
|
case 0xe8d121cb: // "Obj_LookAtPosStored"
|
|
LookAt_Init( pParams, m_stored_pos );
|
|
break;
|
|
|
|
// @script | Obj_LookAtRelPos | look at position relative to object position
|
|
// @uparm (0, 0, 0) | relative position vector
|
|
// @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
|
|
// @parmopt float | time | vel/acceleration | in seconds
|
|
// @parmopt float | speed | 360 | angular velocity in degrees per second
|
|
// @parmopt float | acceleration | 0 | angular acceleration in degrees per second
|
|
// per second
|
|
case 0x33df698f: // "Obj_LookAtRelPos"
|
|
LookAtPos_Init( pParams, false );
|
|
break;
|
|
|
|
// @script | Obj_StopAlongPath | accurately stop and object
|
|
// @uparm 1.0 | distance in feet. Note: doesn't use deceleration
|
|
// set in Obj_SetDeceleration
|
|
case 0xfe71453f: // "Obj_StopAlongPath"
|
|
m_stop_dist = 0;
|
|
m_stop_dist_traveled = 0;
|
|
if ( !m_min_stop_vel )
|
|
{
|
|
m_min_stop_vel = 15;
|
|
}
|
|
pParams->GetFloat( NONAME, &m_stop_dist );
|
|
if ( !pParams->ContainsFlag( 0xf915b4f3 ) ) // inches
|
|
{
|
|
m_stop_dist = FEET_TO_INCHES( m_stop_dist );
|
|
m_vel_z_start = m_vel_z;
|
|
}
|
|
if ( !m_stop_dist )
|
|
{
|
|
m_vel_z = 0;
|
|
}
|
|
m_movingobj_flags |= MOVINGOBJ_FLAG_STOP_ALONG_PATH;
|
|
break;
|
|
|
|
// @script | Obj_StartAlongPath | Only needs to be used if you've
|
|
// stopped an object along a path, to restart the object moving.
|
|
case 0xbd6c6807: // "Obj_StartAlongPath"
|
|
m_movingobj_flags &= ~MOVINGOBJ_FLAG_STOP_ALONG_PATH;
|
|
break;
|
|
|
|
// @script | Obj_FollowPathLinked | Will cause the object to
|
|
// follow the path that it is linked to.
|
|
// When calling Obj_FollowPathLinked, you can add an
|
|
// optional parameter: <nl>
|
|
// originalNode
|
|
// to follow a linked path to the node that created the object. <nl>
|
|
// If random mode is on, a random link from the original
|
|
// node will be used
|
|
// @flag originalNode | follow a linked path to the node that
|
|
// created the object
|
|
case 0x0da2d86d: // "Obj_FollowPathLinked"
|
|
m_movingobj_flags &= ~MOVINGOBJ_FLAG_STOP_ALONG_PATH;
|
|
FollowPathLinked_Init( pParams, pScript );
|
|
break;
|
|
|
|
// @script | Obj_FollowPathStored | Follow the path from current
|
|
// position to the position of the waypoint last stored
|
|
// in the script call to Obj_StoreNode (when a pedestrian, for example, has to jump
|
|
// out of the way of the player, he stores his current position and the current node
|
|
// that he's heading for... then he walks back to his position, and finally ends up
|
|
// heading to the waypoint he was heading for before he left the path to jump out of the way).
|
|
case 0xcc19c48a: // "Obj_FollowPathStored"
|
|
FollowStoredPath_Init( pParams );
|
|
break;
|
|
|
|
// @script | Obj_SetGroundOffset |
|
|
case 0xbf9fffe5: // "Obj_SetGroundOffset"
|
|
Dbg_Message( "Obj_SetGroundOffset is obsolete!" );
|
|
break;
|
|
|
|
// @script | Obj_MoveToPosStored | move to world position stored
|
|
// using Obj_StorePos
|
|
// @parmopt float | time | | default unit is milliseconds
|
|
// @parmopt float | speed | | used instead of time and acceleration
|
|
// @parmopt float | acceleration | | change in speed per second
|
|
case 0x8b669799: // "Obj_MoveToPosStored"
|
|
Move_Init( pParams, pScript, m_stored_pos );
|
|
break;
|
|
|
|
// @script | Obj_MoveForward | Moves a distance in the direction the object is facing.
|
|
// @parmopt float | dist | | Distance to move, in inches.
|
|
// @parmopt float | time | | default unit is milliseconds
|
|
// @parmopt float | speed | | used instead of time and acceleration
|
|
// @parmopt float | acceleration | | change in speed per second
|
|
case 0xf389285d: // Obj_MoveForward
|
|
{
|
|
float dist=0.0f;
|
|
pParams->GetFloat(CRCD(0x7e832f08,"Dist"),&dist);
|
|
Move_Init( pParams, pScript, GetObject()->GetPos() + GetObject()->m_matrix[Z] * dist );
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_MoveLeft | Moves a distance at 90 degrees to the direction the object is facing.
|
|
// @parmopt float | dist | | Distance to move, in inches.
|
|
// @parmopt float | time | | default unit is milliseconds
|
|
// @parmopt float | speed | | used instead of time and acceleration
|
|
// @parmopt float | acceleration | | change in speed per second
|
|
case 0x5ce058fd: // Obj_MoveLeft
|
|
{
|
|
float dist=0.0f;
|
|
pParams->GetFloat(CRCD(0x7e832f08,"Dist"),&dist);
|
|
Move_Init( pParams, pScript, GetObject()->GetPos() + GetObject()->m_matrix[X] * dist );
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_MoveToNode | move to any node by node name
|
|
// @parm name | name | node name
|
|
// @parmopt float | time | | default unit is milliseconds
|
|
// @parmopt float | speed | | used instead of time and acceleration
|
|
// @flag mph | time in mph
|
|
// @flag IPS | time in IPS
|
|
// @flag fps | time in fps
|
|
// @parmopt float | acceleration | | change in speed per second
|
|
case 0x8819dd8b: // "Obj_MoveToNode"
|
|
MoveToNode_Init( pParams, pScript );
|
|
break;
|
|
|
|
// @script | Obj_MoveToPos | moves to the world position specified
|
|
// by the vector
|
|
// @uparm (0, 0, 0) | position vector
|
|
// @parmopt float | time | | default unit is milliseconds
|
|
// @parmopt float | speed | | used instead of time and acceleration
|
|
// @parmopt float | acceleration | | change in speed per second
|
|
case 0x84ec6600: // "Obj_MoveToPos"
|
|
MoveToPos_Init( pParams, pScript, true );
|
|
break;
|
|
|
|
// @script | Obj_MoveToRelPos | moves to a position relative to the object
|
|
// @uparm (0, 0, 0) | position vector
|
|
// @parmopt float | time | | default unit is milliseconds
|
|
// @parmopt float | speed | | used instead of time and acceleration
|
|
// @parmopt float | acceleration | | change in speed per second
|
|
case 0xea81a32b: // "Obj_MoveToRelPos"
|
|
MoveToPos_Init( pParams, pScript, false );
|
|
break;
|
|
|
|
// @script | Obj_MoveToLink | Moves to the 1st node that this object is linked to
|
|
// @parmopt float | time | | default unit is milliseconds
|
|
// @parmopt float | speed | | used instead of time and acceleration
|
|
// @parmopt float | acceleration | | change in speed per second
|
|
// @parm int | LinkNum | link number
|
|
case 0x3bcaac3f: // "Obj_MoveToLink"
|
|
MoveToLink_Init( pParams, pScript );
|
|
break;
|
|
|
|
// @script | Obj_StickToGround |
|
|
// @flag on | turn on (optional - default value is on)
|
|
// @flag off | turn off (default is on)
|
|
// @parm float | distAbove | distance above the object to check
|
|
// for collision
|
|
// @parm float | distBelow | distance below the object to check
|
|
// for collision
|
|
case 0xc5eca638: // "Obj_StickToGround"
|
|
pParams->GetFloat( 0xa8a5a3c7, &m_col_dist_above ); // "distAbove"
|
|
pParams->GetFloat( 0x182d87a7, &m_col_dist_below ); // "distBelow"
|
|
if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
|
|
{
|
|
m_movingobj_flags &= ~MOVINGOBJ_FLAG_STICK_TO_GROUND;
|
|
}
|
|
else
|
|
{
|
|
m_movingobj_flags |= MOVINGOBJ_FLAG_STICK_TO_GROUND;
|
|
if ( ( m_col_dist_above == 0 ) && ( m_col_dist_below == 0 ) )
|
|
{
|
|
Dbg_Message( "\n%s\nTrying to stick to the ground, distAbove and distBelow are zero!!!", pScript->GetScriptInfo() );
|
|
return CBaseComponent::MF_FALSE;
|
|
}
|
|
StickToGround();
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_WaitJumpFinished | Waits for jump to finish, which is when the object
|
|
// has fallen to the same height it was at when the Obj_Jump command was issued.
|
|
case 0x1e094b84: // Obj_WaitJumpFinished
|
|
{
|
|
pScript->SetWait(Script::WAIT_TYPE_OBJECT_JUMP_FINISHED,this);
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_WaitRotate | wait for rotation to complete
|
|
case 0xbdc4c878: // Obj_WaitRotate
|
|
pScript->SetWait(Script::WAIT_TYPE_OBJECT_ROTATE,this);
|
|
break;
|
|
|
|
// @script | Obj_WaitMove | use to wait for move to complete
|
|
case 0xdbfdd02f: // Obj_WaitMove
|
|
pScript->SetWait(Script::WAIT_TYPE_OBJECT_MOVE,this);
|
|
break;
|
|
|
|
// @script | Obj_WaitStop | use to wait for the object to come to a complete stop
|
|
case CRCC( 0x8d95f1e1, "Obj_WaitStop" ):
|
|
pScript->SetWait(Script::WAIT_TYPE_OBJECT_STOP_FINISHED,this);
|
|
break;
|
|
|
|
// @script | Obj_SkipToRestart | skips the object to a restart point
|
|
case 0x3407ad3d: // Obj_SkipToRestart
|
|
{
|
|
uint32 node_name=0;
|
|
Mth::Vector node_pos;
|
|
|
|
pParams->GetChecksum(NONAME,&node_name);
|
|
int node_index = SkateScript::FindNamedNode(node_name);
|
|
Script::CStruct* p_node_data = SkateScript::GetNode(node_index);
|
|
|
|
SkateScript::GetPosition(p_node_data, &node_pos);
|
|
GetObject()->SetPos( node_pos );
|
|
this->OrientToNode(p_node_data);
|
|
|
|
// refresh the model with the new pos/orientation
|
|
Obj::CModelComponent* pModelComponent = (Obj::CModelComponent*)GetModelComponentFromObject( GetObject() );
|
|
pModelComponent->Update();
|
|
}
|
|
break;
|
|
|
|
case 0xe606eba2: // Obj_Hover
|
|
{
|
|
if (pParams->ContainsFlag(CRCD(0xd443a2bc,"Off")))
|
|
{
|
|
UndoHover();
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_HOVERING;
|
|
}
|
|
else
|
|
{
|
|
m_hover_offset=0;
|
|
|
|
pParams->GetFloat(CRCD(0xc9fde32c,"Amp"),&m_hover_amp);
|
|
float f=1.0f;
|
|
pParams->GetFloat(CRCD(0xa80bea4a,"Freq"),&f);
|
|
m_hover_period=(int)(1000.0f/f);
|
|
// Add 10% of randomness so that groups of hovering things slowly
|
|
// go out of phase with each other rather than hovering in unison.
|
|
int random_number = Mth::Rnd(m_hover_period*20/10);
|
|
m_hover_period += random_number-m_hover_period/10;
|
|
|
|
// printf( "hover period = %d random number = %d\n", m_hover_period, random_number );
|
|
|
|
if ( m_hover_period <= 0 )
|
|
{
|
|
// GJ: make sure that hover period is a valid value
|
|
Dbg_MsgAssert( false, ( "Potential divide-by-zero m_hover_period=%d at %s... tell Mick!", m_hover_period, pScript->GetScriptInfo() ) );
|
|
m_hover_period = 1;
|
|
}
|
|
|
|
m_y_before_applying_hover=GetObject()->GetPos()[Y];
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_HOVERING;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_GetSpeed | Gets the object's current speed and puts it into a parameter called speed.
|
|
// By default the units will be miles per hour.
|
|
// @flag ips | Make the speed value be in inches per second
|
|
// @flag fps | Make the speed value be in feet per second
|
|
case 0xe76d73d2: // Obj_GetSpeed
|
|
{
|
|
float speed=0.0f;
|
|
if (m_movingobj_status & MOVINGOBJ_STATUS_MOVETO)
|
|
{
|
|
speed=m_moveto_speed;
|
|
}
|
|
else if ( GetLockObjComponentFromObject(GetObject()) && GetLockObjComponentFromObject(GetObject())->IsLockEnabled() )
|
|
{
|
|
Mth::Vector d=GetObject()->m_pos - GetObject()->m_old_pos;
|
|
float m_time = Tmr::FrameLength(); // dodgy
|
|
if (m_time)
|
|
{
|
|
speed=d.Length()/m_time;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
speed=GetObject()->m_vel.Length();
|
|
}
|
|
|
|
// speed is now in inches per sec.
|
|
|
|
if ( pParams->ContainsFlag( 0xa18b8f32 ) ) // "ips"
|
|
{
|
|
// Nothing to do.
|
|
}
|
|
else if ( pParams->ContainsFlag( 0xaad7c80f ) ) // "fps"
|
|
{
|
|
// Convert to feet per second
|
|
speed/=FEET_TO_INCHES(1.0f);
|
|
}
|
|
else
|
|
{
|
|
// Convert to miles per hour
|
|
speed/=MPH_TO_INCHES_PER_SECOND(1.0f);
|
|
}
|
|
|
|
pScript->GetParams()->AddFloat("Speed",speed);
|
|
break;
|
|
}
|
|
|
|
|
|
// @script | Obj_Jump | Makes the object jump.
|
|
// @parmopt float | Speed | 150.0 | The upwards speed of the jump
|
|
// @parmopt float | Gravity | -360.0 | The gravity applied to the jump
|
|
// @parmopt vector | heading | | use this heading as jump direction rather than
|
|
// jumping straight up
|
|
// @parmopt float | col_dist_above | 6 | distance used for collision checks above the object
|
|
// @parmopt float | col_dist_below | 6 | distance used for collision checks below the object
|
|
// @flag use_current_heading | use the object's current Z vector as jump direction
|
|
case 0x1ae46261: // Obj_Jump
|
|
{
|
|
m_jump_speed = 150.0f;
|
|
pParams->GetFloat( CRCD(0xf0d90109,"Speed"), &m_jump_speed );
|
|
m_jump_original_speed = m_jump_speed;
|
|
|
|
m_jump_gravity = -360.0f;
|
|
pParams->GetFloat( CRCD(0xa5e2da58,"Gravity"), &m_jump_gravity );
|
|
|
|
m_jump_start_pos = GetObject()->m_pos;
|
|
|
|
m_jump_col_dist_above = 6.0f;
|
|
pParams->GetFloat( CRCD( 0xecc2c699, "col_dist_above" ), &m_jump_col_dist_above, Script::NO_ASSERT );
|
|
m_jump_col_dist_below = 6.0f;
|
|
pParams->GetFloat( CRCD( 0x5c4ae2f9, "col_dist_below" ), &m_jump_col_dist_below, Script::NO_ASSERT );
|
|
|
|
if ( pParams->GetVector( CRCD( 0xfd4bc03e, "heading" ), &m_jump_heading, Script::NO_ASSERT ) )
|
|
{
|
|
m_jump_use_heading = true;
|
|
m_jump_speed *= m_jump_heading[Y];
|
|
m_jump_heading[Y] = 0.0f;
|
|
}
|
|
else if ( pParams->ContainsFlag( CRCD( 0xfecffe49, "use_current_heading" ) ) )
|
|
{
|
|
// m_jump_heading = Mth::Vector(0, 0, 1);
|
|
m_jump_heading = GetObject()->GetDisplayMatrix()[Z];
|
|
m_jump_use_heading = true;
|
|
m_jump_speed *= m_jump_heading[Y];
|
|
m_jump_heading[Y] = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
m_jump_use_heading = false;
|
|
}
|
|
|
|
m_jump_use_land_height = false;
|
|
if ( pParams->GetFloat( CRCD( 0xa1810c27, "land_height" ), &m_jump_land_height, Script::NO_ASSERT ) )
|
|
{
|
|
m_jump_use_land_height = true;
|
|
}
|
|
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_JUMPING;
|
|
break;
|
|
}
|
|
|
|
// @script | Obj_FollowLeader | Makes the object follow a fixed distance behind another object.
|
|
// @parm name | Name | Name of the object to follow
|
|
// @parmopt float | Distance | 100 | Distance behind the leader in inches
|
|
// @flag OrientY | Tilt the object to be at right angles to the path direction
|
|
// @flag Off | Switch off following
|
|
// @flag LeaveYUnaffected | The y coordinate of the object will be left unaffected, so
|
|
// that the object can be clamped to the ground and not follow the skater into the air
|
|
// for example.
|
|
case 0x2fad370d: // Obj_FollowLeader
|
|
FollowLeader_Init( pParams );
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
return CBaseComponent::MF_NOT_EXECUTED;
|
|
}
|
|
|
|
return CBaseComponent::MF_TRUE;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::EnsurePathobExists( CCompositeObject* pCompositeObject )
|
|
{
|
|
if ( !mp_pathOb )
|
|
{
|
|
mp_pathOb = new CPathOb( pCompositeObject );
|
|
Dbg_MsgAssert( mp_pathOb,( "Couldn't allocate pathob." ));
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CPathOb* CMotionComponent::GetPathOb()
|
|
{
|
|
return mp_pathOb;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Added by Ken. If the object is following a path, this will return the node
|
|
// index of the node it is heading towards. Returns -1 otherwise.
|
|
int CMotionComponent::GetDestinationPathNode()
|
|
{
|
|
if ( !(m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH) )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if ( !mp_pathOb )
|
|
{
|
|
return -1;
|
|
}
|
|
//Dbg_MsgAssert(mp_pathOb,("NULL GetPathOb() ?"));
|
|
return mp_pathOb->m_node_to;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int CMotionComponent::GetPreviousPathNode()
|
|
{
|
|
if ( !(m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH) )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if ( !mp_pathOb )
|
|
{
|
|
return -1;
|
|
}
|
|
//Dbg_MsgAssert(mp_pathOb,("NULL GetPathOb() ?"));
|
|
return mp_pathOb->m_node_from;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::Reset()
|
|
{
|
|
m_turn_ang_target.Set( );
|
|
m_cur_turn_ang.Set( );
|
|
m_turn_speed_target.Set( );
|
|
m_turn_speed.Set( );
|
|
m_delta_turn_speed.Set( );
|
|
m_angular_friction.Set( );
|
|
m_orig_pos.Set( );
|
|
|
|
m_moveto_pos.Set( );
|
|
m_moveto_dist = 0.0f;
|
|
m_moveto_dist_traveled = 0.0f;
|
|
m_moveto_speed = 0.0f;
|
|
m_moveto_speed_target = 0.0f;
|
|
m_moveto_acceleration = 0.0f;
|
|
|
|
m_acceleration = 0.0f;
|
|
m_deceleration = 0.0f;
|
|
m_max_vel = 0.0f;
|
|
m_vel_z = 0.0f;
|
|
m_stop_dist = 0.0f;
|
|
m_stop_dist_traveled = 0.0f;
|
|
m_vel_z_start = 0.0f;
|
|
m_min_stop_vel = 0.0f;
|
|
|
|
m_rot_time_target = 0.0f;
|
|
m_rot_time = 0.0f;
|
|
|
|
// don't think this is dangerous...
|
|
m_movingobj_status = 0;
|
|
m_movingobj_flags = 0;
|
|
|
|
// Reset the Triangle collision caches
|
|
|
|
m_last_triangle.Init();
|
|
m_car_left_rear_last_triangle.Init();
|
|
m_car_right_rear_last_triangle.Init();
|
|
m_car_mid_front_last_triangle.Init();
|
|
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::SetCorrectRotationDirection( int rotAxis )
|
|
{
|
|
|
|
m_turn_speed[ rotAxis ] = Mth::Abs( m_turn_speed[ rotAxis ] );
|
|
m_turn_speed_target[ rotAxis ] = Mth::Abs( m_turn_speed_target[ rotAxis ] );
|
|
m_delta_turn_speed[ rotAxis ] = Mth::Abs( m_delta_turn_speed[ rotAxis ] );
|
|
m_angular_friction[ rotAxis ] = Mth::Abs( m_angular_friction[ rotAxis ] );
|
|
if ( m_turn_ang_target[ rotAxis ] < 0 )
|
|
{
|
|
m_turn_speed[ rotAxis ] *= -1.0f;
|
|
m_turn_speed_target[ rotAxis ] *= -1.0f;
|
|
m_delta_turn_speed[ rotAxis ] *= -1.0f;
|
|
m_angular_friction[ rotAxis ] *= -1.0f;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::InitPath( int nodeNumber )
|
|
{
|
|
if (mp_path_object_tracker)
|
|
{
|
|
mp_path_object_tracker->StopTrackingObject( GetObject() );
|
|
}
|
|
|
|
mp_path_object_tracker=Obj::CPathMan::Instance()->TrackObject( GetObject(), nodeNumber );
|
|
// Note: mp_path_object_tracker could be NULL
|
|
|
|
EnsurePathobExists( GetObject() );
|
|
GetPathOb()->NewPath( nodeNumber, GetObject()->GetPos() );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::FollowPath_Init( Script::CStruct* pParams )
|
|
{
|
|
uint32 nodeChecksum;
|
|
if ( pParams->GetChecksum( 0xa1dc81f9, &nodeChecksum ) ) // "name"
|
|
{
|
|
int nodeNum = SkateScript::FindNamedNode( nodeChecksum );
|
|
InitPath( nodeNum );
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH;
|
|
// these can't coexist:
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
|
|
}
|
|
else
|
|
{
|
|
Dbg_MsgAssert( 0,( "Must specify name of node in Obj_FollowPath, using name = nodeName." ));
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CMotionComponent::PickRandomWaypoint()
|
|
{
|
|
return ( m_movingobj_flags & MOVINGOBJ_FLAG_RANDOM_PATH_MODE );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::HitWaypoint( int nodeIndex )
|
|
{
|
|
uint32 scriptChecksum;
|
|
Script::CStruct* pNodeData;
|
|
pNodeData = SkateScript::GetNode( nodeIndex );
|
|
Dbg_MsgAssert( pNodeData,( "Waypoint gave us a bad node index." ));
|
|
m_current_node = nodeIndex;
|
|
|
|
if ( pNodeData->GetChecksum( 0x3aefe377, &scriptChecksum ) ) // "SpawnObjScript"
|
|
{
|
|
Script::CStruct* pScriptParams = NULL;
|
|
pNodeData->GetStructure( CRCD(0x7031f10c,"Params"), &pScriptParams );
|
|
#ifdef __NOPT_ASSERT__
|
|
Script::CScript* p_script=GetObject()->SpawnScriptPlease( scriptChecksum, pScriptParams );
|
|
p_script->SetCommentString("Created by CMotionComponent::HitWaypoint");
|
|
#else
|
|
GetObject()->SpawnScriptPlease( scriptChecksum, pScriptParams );
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if ( pNodeData->GetChecksum( 0x86125749, &scriptChecksum ) ) // "SwitchObjScript"
|
|
{
|
|
Script::CStruct* pScriptParams = NULL;
|
|
pNodeData->GetStructure( CRCD(0x7031f10c,"Params"), &pScriptParams );
|
|
GetObject()->SwitchScript( scriptChecksum, pScriptParams );
|
|
return;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CMotionComponent::LookAt_Init( Script::CStruct* pParams, const Mth::Vector& lookAtPos )
|
|
{
|
|
int deltaDegreesPerSecond = 360;
|
|
int deltaDeltaDegreesPerSecond = 0;
|
|
int lockAxis = ( 1 << Y );
|
|
float time = 0.0f;
|
|
|
|
if ( !( m_movingobj_status & MOVINGOBJ_STATUS_LOOKAT ) )
|
|
{
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_LOOKAT;
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_ROTXYZ;
|
|
m_movingobj_flags &= ~( MOVINGOBJ_FLAG_CONST_ROTX | MOVINGOBJ_FLAG_CONST_ROTY | MOVINGOBJ_FLAG_CONST_ROTZ );
|
|
}
|
|
|
|
pParams->GetFloat( 0x906b67ba, &time ); // "time"
|
|
if ( !time )
|
|
{
|
|
pParams->GetInteger( 0xf0d90109, &deltaDegreesPerSecond ); // 'speed'
|
|
pParams->GetInteger( 0x85894a84, &deltaDeltaDegreesPerSecond ); // 'acceleration'
|
|
pParams->GetInteger( 0xa4aecb20, &lockAxis ); // "lockAxis"
|
|
}
|
|
|
|
float threshold=0.0f;
|
|
pParams->GetFloat(0xf73a4efd,&threshold); // AngleThreshold
|
|
threshold=DEGREES_TO_RADIANS(threshold);
|
|
|
|
bool rotation_required=false;
|
|
int i;
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
if ( lockAxis & ( 1 << i ) )
|
|
{
|
|
if ( SetUpLookAtPos( lookAtPos, GetObject()->GetPos(), i == Z ? Y : Z, i, threshold ) )
|
|
{
|
|
if ( time )
|
|
{
|
|
m_delta_turn_speed[ i ] = 0.0f;
|
|
m_turn_speed[ i ] = m_turn_ang_target[ i ] / time;
|
|
}
|
|
else
|
|
{
|
|
m_delta_turn_speed[ i ] = DEGREES_TO_RADIANS( deltaDeltaDegreesPerSecond );
|
|
if ( !m_delta_turn_speed[ i ] )
|
|
m_turn_speed[ i ] = DEGREES_TO_RADIANS( deltaDegreesPerSecond );
|
|
else
|
|
{
|
|
m_turn_speed[ i ] = 0.0f;
|
|
m_turn_speed_target[ i ] = 0.0f;
|
|
}
|
|
SetCorrectRotationDirection( i );
|
|
}
|
|
rotation_required=true;
|
|
}
|
|
}
|
|
}
|
|
return rotation_required;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::QuatRot_Setup( Script::CStruct* pParams, const Mth::Matrix& rot )
|
|
{
|
|
if ( !mp_slerp )
|
|
{
|
|
mp_slerp = new Mth::SlerpInterpolator;
|
|
}
|
|
|
|
Mth::Matrix tempMatrix = rot;
|
|
mp_slerp->setMatrices( &GetObject()->GetMatrix(), &tempMatrix );
|
|
|
|
m_rot_time = 0.0f;
|
|
m_rot_time_target = 0.0f;
|
|
pParams->GetFloat( 0x906b67ba, &m_rot_time_target ); // "time" ( in seconds )
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_QUAT_ROT;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::QuatRot_Init( Script::CStruct* pParams )
|
|
{
|
|
Mth::Vector rot( 0.0f, 0.0f, 0.0f );
|
|
if ( pParams->GetVector( 0x91a4c826, &rot ) ) // relative
|
|
{
|
|
if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
|
|
{
|
|
rot[ Z ] = -rot[ Z ];
|
|
rot.ConvertToMaxCoords( );
|
|
}
|
|
rot.DegreesToRadians( );
|
|
Mth::Matrix temp = GetObject()->GetMatrix();
|
|
temp.RotateLocal( rot );
|
|
QuatRot_Setup( pParams, temp );
|
|
}
|
|
else if ( pParams->GetVector( 0x592089a9, &rot ) ) //absolute
|
|
{
|
|
if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
|
|
{
|
|
rot[ Z ] = -rot[ Z ];
|
|
rot.ConvertToMaxCoords( );
|
|
}
|
|
rot.DegreesToRadians( );
|
|
Mth::Matrix temp( rot[ X ], rot[ Y ], rot[ Z ] );
|
|
QuatRot_Setup( pParams, temp );
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CMotionComponent::SetUpLookAtPos( const Mth::Vector& lookToPos, const Mth::Vector& currentPos, int headingAxis, int rotAxis, float threshold )
|
|
{
|
|
Mth::Vector pathHeading = lookToPos - currentPos;
|
|
|
|
m_cur_turn_ang[ rotAxis ] = 0.0f;
|
|
m_turn_ang_target[ rotAxis ] = Mth::GetAngle( GetObject()->GetMatrix(), pathHeading, headingAxis, rotAxis );
|
|
if ( fabs(m_turn_ang_target[ rotAxis ]) > threshold )
|
|
{
|
|
m_movingobj_status |= ( MOVINGOBJ_STATUS_ROTX << rotAxis );
|
|
/* Dbg_MsgAssert( ( ( !( m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH ) ) ||
|
|
( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) ) ||
|
|
rotAxis == Z,( "Can't rotate on X or Y axis while using auto-heading on path." ));*/
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::RotAxis_Init( Script::CStruct* pParams, Script::CScript* pScript, int rotAxis )
|
|
{
|
|
|
|
float mult = 1.0f;
|
|
|
|
// Mick - Just being safe here.
|
|
Dbg_MsgAssert( rotAxis == X || rotAxis == Y || rotAxis== Z,( "Illegal rotAxis %d", rotAxis ));
|
|
|
|
|
|
// get out of lookat mode... and shit.
|
|
if ( m_movingobj_status & MOVINGOBJ_STATUS_LOOKAT )
|
|
{
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_LOOKAT;
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_ROTXYZ;
|
|
}
|
|
if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
|
|
{
|
|
switch ( rotAxis )
|
|
{
|
|
case ( X ):
|
|
mult = -1.0f;
|
|
break;
|
|
|
|
case ( Y ):
|
|
rotAxis = Z;
|
|
// mult = -1.0f;
|
|
break;
|
|
|
|
case ( Z ):
|
|
rotAxis = Y;
|
|
break;
|
|
|
|
default:
|
|
Dbg_MsgAssert( 0,( "Illegal rotAxis" ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// clear this in case it isn't set...
|
|
m_angular_friction[ rotAxis ] = 0.0f;
|
|
m_delta_turn_speed[ rotAxis ] = 0.0f;
|
|
m_turn_ang_target[ rotAxis ] = 0.0f;
|
|
|
|
if ( pParams->GetFloat( 0xff7ebaf6, &m_turn_ang_target[ rotAxis ] ) ) // "angle"
|
|
{
|
|
m_turn_ang_target[ rotAxis ] = mult * DEGREES_TO_RADIANS( m_turn_ang_target[ rotAxis ] );
|
|
m_movingobj_flags &= ~( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis );
|
|
m_cur_turn_ang[ rotAxis ] = 0.0f;
|
|
m_turn_speed[ rotAxis ] = 0.0f;
|
|
if ( !m_turn_ang_target[ rotAxis ] )
|
|
{
|
|
// stop the rotation!
|
|
m_movingobj_status &= ~( MOVINGOBJ_STATUS_ROTX << rotAxis );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( pParams->GetFloat( 0xf59ff7d7, &m_angular_friction[ rotAxis ] ) ) // "deceleration"
|
|
{
|
|
// K: If a deceleration is specified, the 'speed' value specified is the target speed.
|
|
// The current m_turn_speed remains unchanged so that it slows down to the target speed.
|
|
// (TT427)
|
|
m_angular_friction[ rotAxis ] = DEGREES_TO_RADIANS( m_angular_friction[ rotAxis ] );
|
|
if ( pParams->GetFloat( 0xf0d90109, &m_turn_speed_target[ rotAxis ] ) ) // "speed"
|
|
{
|
|
m_turn_speed_target[ rotAxis ] = DEGREES_TO_RADIANS( m_turn_speed_target[ rotAxis ] );
|
|
}
|
|
else
|
|
{
|
|
Dbg_MsgAssert( 0,( "\n%s\nMust specify speed for Obj_Rot functions (degrees per second).", pScript->GetScriptInfo( ) ));
|
|
}
|
|
}
|
|
else if ( pParams->GetFloat( 0x85894a84, &m_delta_turn_speed[ rotAxis ] ) ) // acceleration
|
|
{
|
|
m_delta_turn_speed[ rotAxis ] = DEGREES_TO_RADIANS( m_delta_turn_speed[ rotAxis ] );
|
|
if ( pParams->GetFloat( 0xf0d90109, &m_turn_speed_target[ rotAxis ] ) ) // "speed"
|
|
{
|
|
m_turn_speed_target[ rotAxis ] = DEGREES_TO_RADIANS( m_turn_speed_target[ rotAxis ] );
|
|
}
|
|
else
|
|
{
|
|
Dbg_MsgAssert( 0,( "\n%s\nMust specify speed for Obj_Rot functions (degrees per second).", pScript->GetScriptInfo( ) ));
|
|
}
|
|
if ( !( m_movingobj_status & ( MOVINGOBJ_STATUS_ROTX << rotAxis ) ) )
|
|
{
|
|
m_turn_speed[ rotAxis ] = 0.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pParams->GetFloat( 0xf0d90109, &m_turn_speed[ rotAxis ] ) ) // "speed"
|
|
{
|
|
m_turn_speed[ rotAxis ] = DEGREES_TO_RADIANS( m_turn_speed[ rotAxis ] );
|
|
}
|
|
else
|
|
{
|
|
Dbg_MsgAssert( 0,( "\n%s\nMust specify speed for Obj_Rot functions (degrees per second).", pScript->GetScriptInfo( ) ));
|
|
}
|
|
}
|
|
if ( !( m_turn_ang_target[ rotAxis ] || m_angular_friction[ rotAxis ] ) )
|
|
{
|
|
m_movingobj_flags |= ( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis );
|
|
}
|
|
if ( !( m_movingobj_flags & ( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis ) ) )
|
|
{
|
|
SetCorrectRotationDirection( rotAxis );
|
|
}
|
|
m_movingobj_status |= ( MOVINGOBJ_STATUS_ROTX << rotAxis );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::LookAtNodeLinked_Init( Script::CStruct* pParams, Script::CScript* pScript )
|
|
{
|
|
int numLinks;
|
|
numLinks = SkateScript::GetNumLinks( m_current_node );
|
|
if ( !numLinks )
|
|
{
|
|
Dbg_MsgAssert( 0,( "\n%s\nNo node linked to object calling Obj_LookAtNodeLinked", pScript->GetScriptInfo( ) ));
|
|
return;
|
|
}
|
|
|
|
int link_num=1;
|
|
pParams->GetInteger( 0xe85997d0, &link_num ); // "linkNum"
|
|
// Designers number the links from 1 to n, so convert to programmer form
|
|
link_num -= 1;
|
|
|
|
// Print a warning message instead of asserting, to save having to reboot.
|
|
if (link_num<0 || link_num>=numLinks)
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
printf("!!!!!!!!!! Bad LinkNum of %d sent to Obj_LookAtNodeLinked, range is 1 to %d\n",link_num+1,numLinks);
|
|
#endif
|
|
link_num=0;
|
|
}
|
|
|
|
Mth::Vector lookAtPos;
|
|
SkateScript::GetPosition( SkateScript::GetLink( m_current_node, link_num ), &lookAtPos );
|
|
LookAt_Init( pParams, lookAtPos );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::LookAtNode_Init( Script::CStruct* pParams, Script::CScript* pScript )
|
|
{
|
|
uint32 checksum = 0;
|
|
|
|
pParams->GetChecksum( 0xa1dc81f9, &checksum ); // "name"
|
|
if ( !checksum )
|
|
{
|
|
Dbg_MsgAssert( 0,( "\n%s\nNeed node name in Obj_LookAtNode", pScript->GetScriptInfo( ) ));
|
|
return;
|
|
}
|
|
Mth::Vector lookAtPos;
|
|
SkateScript::GetPosition( SkateScript::FindNamedNode( checksum ), &lookAtPos );
|
|
LookAt_Init( pParams, lookAtPos );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::LookAtPos_Init( Script::CStruct* pParams, bool absolute )
|
|
{
|
|
|
|
Mth::Vector vec( 0, 0, 0 );
|
|
pParams->GetVector( NONAME, &vec );
|
|
if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
|
|
{
|
|
vec.ConvertToMaxCoords( );
|
|
}
|
|
if ( !absolute )
|
|
{
|
|
vec += GetObject()->GetPos();
|
|
}
|
|
LookAt_Init( pParams, vec );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::Rot( int rotAxis, float deltaRot )
|
|
{
|
|
switch ( rotAxis )
|
|
{
|
|
case ( X ):
|
|
GetObject()->m_matrix.RotateXLocal( deltaRot );
|
|
GetObject()->m_matrix.OrthoNormalizeAbout( X );
|
|
break;
|
|
|
|
case ( Y ):
|
|
GetObject()->m_matrix.RotateYLocal( deltaRot );
|
|
GetObject()->m_matrix.OrthoNormalizeAbout( Y );
|
|
break;
|
|
|
|
case ( Z ):
|
|
GetObject()->m_matrix.RotateZLocal( deltaRot );
|
|
GetObject()->m_matrix.OrthoNormalizeAbout( Z );
|
|
break;
|
|
|
|
default:
|
|
Dbg_MsgAssert( 0,( "illegal rot axis." ));
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::RotateToFacePos( const Mth::Vector pos )
|
|
{
|
|
Mth::Vector desiredHeading;
|
|
desiredHeading = pos - GetObject()->GetPos();
|
|
float deltaY = Mth::GetAngle( GetObject()->GetMatrix(), desiredHeading );
|
|
GetObject()->m_matrix.RotateYLocal( deltaY );
|
|
GetObject()->m_matrix.OrthoNormalizeAbout( Y );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Follow the path that's linked to the current waypoint (node) that the object is sitting at.
|
|
void CMotionComponent::FollowPathLinked_Init( Script::CStruct* pParams, Script::CScript* pScript )
|
|
{
|
|
int numLinks;
|
|
int nodeNum;
|
|
|
|
if ( pParams->ContainsFlag( 0x422e1fce ) ) // originalNode
|
|
{
|
|
nodeNum = m_start_node;
|
|
}
|
|
else
|
|
{
|
|
nodeNum = m_current_node;
|
|
}
|
|
|
|
numLinks = SkateScript::GetNumLinks( nodeNum );
|
|
if ( !numLinks )
|
|
{
|
|
Dbg_MsgAssert( 0,( "\n%s\nNo node linked to object (from node %d) calling Obj_FollowPathLinked", pScript->GetScriptInfo( ), nodeNum ));
|
|
return;
|
|
}
|
|
if ( ( numLinks > 1 ) && PickRandomWaypoint( ) )
|
|
{
|
|
InitPath( SkateScript::GetLink( nodeNum, Mth::Rnd( numLinks ) ) );
|
|
}
|
|
else
|
|
{
|
|
InitPath( SkateScript::GetLink( nodeNum, 0 ) );
|
|
}
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH;
|
|
// these can't coexist:
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Follow the path from current position to the position of the waypoint last stored
|
|
// in the script call to Obj_StoreNode (when a pedestrian, for example, has to jump
|
|
// out of the way of the player, he stores his current position and the current node
|
|
// that he's heading for... then he walks back to his position, and finally ends up
|
|
// heading to the waypoint he was heading for before he left the path to jump out of the way).
|
|
void CMotionComponent::FollowStoredPath_Init( Script::CStruct* pParams )
|
|
{
|
|
InitPath( m_stored_node );
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH;
|
|
// these can't coexist:
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Physics for when the moving object is following a path.
|
|
void CMotionComponent::DoPathPhysics( void )
|
|
{
|
|
if ( m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH )
|
|
{
|
|
// if we're stopping:
|
|
if ( m_movingobj_flags & MOVINGOBJ_FLAG_STOP_ALONG_PATH )
|
|
{
|
|
if ( m_stop_dist_traveled >= m_stop_dist )
|
|
{
|
|
return;
|
|
}
|
|
// keep slowing down...
|
|
m_stop_dist_traveled += m_vel_z * m_time;
|
|
if ( ( !m_stop_dist ) || ( m_stop_dist_traveled >= m_stop_dist ) )
|
|
{
|
|
m_vel_z = 0;
|
|
}
|
|
else
|
|
{
|
|
float percent = m_stop_dist_traveled / m_stop_dist;
|
|
m_vel_z = ( m_vel_z_start * ( 1.0f - percent ) ) + ( m_min_stop_vel * ( percent ) );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Adjust velocity...
|
|
if ( m_vel_z < m_max_vel )
|
|
{
|
|
if ( m_acceleration )
|
|
{
|
|
m_vel_z += ( m_acceleration * m_time );
|
|
if ( m_vel_z > m_max_vel )
|
|
{
|
|
m_vel_z = m_max_vel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_vel_z = m_max_vel;
|
|
}
|
|
}
|
|
else if ( m_vel_z > m_max_vel )
|
|
{
|
|
if ( m_deceleration )
|
|
{
|
|
m_vel_z -= ( m_deceleration * m_time );
|
|
if ( m_vel_z < m_max_vel )
|
|
{
|
|
m_vel_z = m_max_vel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_vel_z = m_max_vel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
#if 0
|
|
|
|
// UNUSED...
|
|
|
|
// BB: I have added a new version of this function below. I moved the code
|
|
// from the CallMemberFunction func because I needed to do the same thing from c.
|
|
// I have left this here for reference, just in case.
|
|
CCompositeObject* CMotionComponent::GetNextObjectOnPath( float range )
|
|
{
|
|
if (!(m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH) || !GetPathOb() || !mp_path_object_tracker)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
float distance_covered=0.0f;
|
|
Mth::Vector path_pos=m_pos;
|
|
int next_node=GetPathOb()->m_node_to;
|
|
|
|
// Copy the non-NULL object pointers from the tracker to a small local array for speed,
|
|
// since they may need to be stepped through several times and the tracker's array
|
|
// contains lots of NULL's
|
|
#define MAX_OBS 20
|
|
CCompositeObject* pp_objects[MAX_OBS];
|
|
int num_objects=0;
|
|
|
|
const CSmtPtr<CCompositeObject>* pp_object_list=mp_path_object_tracker->GetObjectList();
|
|
for (int i=0; i<CPathObjectTracker::MAX_OBJECTS_PER_PATH; ++i)
|
|
{
|
|
if (pp_object_list[i])
|
|
{
|
|
Dbg_MsgAssert(num_objects<MAX_OBS,("Too many objects on path, need to increase MAX_OBS in CMovingObject::GetNextObjectOnPath"));
|
|
pp_objects[num_objects++]=pp_object_list[i].Convert();
|
|
}
|
|
}
|
|
|
|
CCompositeObject* p_nearest=NULL;
|
|
while (true)
|
|
{
|
|
float min_dist=0.0f;
|
|
|
|
for (int i=0; i<num_objects; ++i)
|
|
{
|
|
if (GetMotionComponentFromObject(pp_objects[i])->GetDestinationPathNode()==next_node)
|
|
{
|
|
float d=Mth::Distance(pp_objects[i]->m_pos,path_pos);
|
|
if (distance_covered+d < range)
|
|
{
|
|
if (!p_nearest || d<min_dist)
|
|
{
|
|
min_dist=d;
|
|
p_nearest=pp_objects[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p_nearest)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Script::CStruct* p_node=SkateScript::GetNode(next_node);
|
|
Dbg_MsgAssert(p_node,("NULL p_node"));
|
|
Mth::Vector node_pos;
|
|
SkateScript::GetPosition(p_node,&node_pos);
|
|
|
|
distance_covered+=Mth::Distance(path_pos,node_pos);
|
|
path_pos=node_pos;
|
|
|
|
// Get the next linked node
|
|
Script::CArray* p_links=NULL;
|
|
p_node->GetArray(0x2e7d5ee7/*Links*/,&p_links);
|
|
|
|
if (!p_links)
|
|
{
|
|
break;
|
|
}
|
|
|
|
next_node=p_links->GetInteger(0);
|
|
}
|
|
|
|
return p_nearest;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// returns TRUE if instant move, FALSE otherwise.
|
|
bool CMotionComponent::Move_Init( Script::CStruct* pParams, Script::CScript* pScript, Mth::Vector pos, int nodeNum )
|
|
{
|
|
int units = UNITS_MPH;
|
|
float time;
|
|
float speed;
|
|
|
|
m_moveto_pos = pos;
|
|
m_moveto_dist = Mth::Distance( m_moveto_pos, GetObject()->GetPos() );
|
|
m_moveto_acceleration = 0.0f;
|
|
|
|
// initialize everything:
|
|
m_orig_pos = GetObject()->GetPos();
|
|
m_moveto_dist_traveled = 0.0f;
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_MOVETO;
|
|
// can't follow path and moveto at the same time:
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_ON_PATH;
|
|
m_moveto_node_num = -1;
|
|
if ( pParams->GetFloat( 0x906b67ba, &time ) ) // "time"
|
|
{
|
|
// time is in seconds:
|
|
Dbg_MsgAssert( time,( "\n%s\nCan't have zero for time...", pScript->GetScriptInfo( ) ));
|
|
m_moveto_speed = m_moveto_dist / time;
|
|
}
|
|
else if ( pParams->GetFloat( 0xf0d90109, &speed ) ) // "speed"
|
|
{ // use speed and acceleration:
|
|
if ( pParams->ContainsFlag( 0x2ce7ee02 ) ) // "mph"
|
|
{
|
|
units = UNITS_MPH;
|
|
}
|
|
else if ( pParams->ContainsFlag( 0xaad7c80f ) ) // "fps"
|
|
{
|
|
units = UNITS_FPS;
|
|
}
|
|
else if ( pParams->ContainsFlag( 0xa18b8f32 ) ) //IPS
|
|
{
|
|
units = UNITS_IPS;
|
|
}
|
|
|
|
pParams->GetFloat( 0x85894a84, &m_moveto_acceleration ); // "acceleration"
|
|
|
|
if ( units == UNITS_MPH )
|
|
{
|
|
speed = MPH_TO_IPS( speed );
|
|
m_moveto_acceleration = MPH_TO_IPS( m_moveto_acceleration );
|
|
}
|
|
else if ( units == UNITS_FPS )
|
|
{
|
|
speed *= 12.0f;
|
|
m_moveto_acceleration *= 12.0f;
|
|
}
|
|
|
|
if ( !m_moveto_acceleration )
|
|
{
|
|
m_moveto_speed = speed;
|
|
}
|
|
else
|
|
{
|
|
m_moveto_speed = 0.0f;
|
|
m_moveto_speed_target = speed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// instantaneous move:
|
|
GetObject()->SetPos(m_moveto_pos);
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
|
|
|
|
if ( nodeNum != -1 )
|
|
{
|
|
if ( pParams->ContainsFlag( 0x90a91232 ) ) // "orient"
|
|
{
|
|
Script::CStruct* pNodeData = SkateScript::GetNode( nodeNum );
|
|
OrientToNode( pNodeData );
|
|
}
|
|
}
|
|
|
|
// GJ: Need to re-implement this on THPS5...
|
|
// (used to fix quick-jump bug on Xbox/NGC,
|
|
// where LODing code wasn't refreshing the model's
|
|
// position)
|
|
|
|
#if 0
|
|
#ifndef __PLAT_NGPS__
|
|
// Rebuild display matrix. Gary - you should look at this post THPS4 and generalize.
|
|
Mth::Matrix rootMatrix;
|
|
rootMatrix = m_display_matrix;
|
|
rootMatrix[Mth::POS] = m_pos;
|
|
rootMatrix[Mth::POS][W] = 1.0f;
|
|
if( GetModel() )
|
|
{
|
|
GetModel()->Render( &rootMatrix, true, GetSkeleton() );
|
|
|
|
// Also update the shadow if relevant.
|
|
if( mp_shadow && mp_shadow->GetShadowType() != Gfx::vDETAILED_SHADOW )
|
|
{
|
|
update_shadow();
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::MoveToNode_Init( Script::CStruct* pParams, Script::CScript* pScript )
|
|
{
|
|
Mth::Vector pos;
|
|
|
|
// if we're moving to a named node:
|
|
uint32 checksum = 0;
|
|
pParams->GetChecksum( 0xa1dc81f9, &checksum ); // "name"
|
|
if ( checksum )
|
|
{
|
|
int nodeNum = SkateScript::FindNamedNode( checksum );
|
|
SkateScript::GetPosition( nodeNum, &pos );
|
|
if ( Move_Init( pParams, pScript, pos, nodeNum ) )
|
|
{
|
|
HitWaypoint( nodeNum );
|
|
}
|
|
else
|
|
{
|
|
m_moveto_node_num = nodeNum;
|
|
}
|
|
return;
|
|
}
|
|
Dbg_MsgAssert( 0,( "\n%s\nObj_MoveToNode requires node name specification (name = *)", pScript->GetScriptInfo( ) ));
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::MoveToPos_Init( Script::CStruct* pParams, Script::CScript* pScript, bool absolute )
|
|
{
|
|
Mth::Vector vec( 0, 0, 0 );
|
|
pParams->GetVector( NONAME, &vec );
|
|
if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
|
|
{
|
|
vec.ConvertToMaxCoords( );
|
|
}
|
|
if ( !absolute )
|
|
{
|
|
vec += GetObject()->GetPos();
|
|
}
|
|
Move_Init( pParams, pScript, vec );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::MoveToLink_Init( Script::CStruct* pParams, Script::CScript* pScript )
|
|
{
|
|
int linkNum = 0;
|
|
int numLinks;
|
|
numLinks = SkateScript::GetNumLinks( m_current_node );
|
|
if ( !numLinks )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Obj_MoveToLink requires a link to the object's current node (last node hit in path)." ));
|
|
return;
|
|
}
|
|
if ( pParams->GetInteger( 0xe85997d0, &linkNum ) ) // "linkNum"
|
|
{
|
|
linkNum -= 1;
|
|
}
|
|
else if ( ( numLinks > 1 ) && ( pParams->ContainsFlag( 0x90a1c52a ) ) ) // "randomLink"
|
|
{
|
|
linkNum = Mth::Rnd( numLinks );
|
|
}
|
|
Mth::Vector pos;
|
|
Dbg_MsgAssert( linkNum < numLinks,( "\n%s\nLink num is greater than the number of links.", pScript->GetScriptInfo( ) ));
|
|
int nodeNum;
|
|
nodeNum = SkateScript::GetLink( m_current_node, linkNum );
|
|
SkateScript::GetPosition( nodeNum, &pos );
|
|
if ( Move_Init( pParams, pScript, pos ) )
|
|
{
|
|
HitWaypoint( nodeNum );
|
|
}
|
|
else
|
|
{
|
|
m_moveto_node_num = nodeNum;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CMotionComponent::Move( void )
|
|
{
|
|
if ( m_moveto_acceleration )
|
|
{
|
|
m_moveto_speed += m_moveto_acceleration * Tmr::FrameRatio( );
|
|
if ( m_moveto_speed >= m_moveto_speed_target )
|
|
{
|
|
m_moveto_speed = m_moveto_speed_target;
|
|
m_moveto_acceleration = 0.0f;
|
|
}
|
|
}
|
|
m_moveto_dist_traveled += m_time * m_moveto_speed;
|
|
if ( m_moveto_dist_traveled >= m_moveto_dist )
|
|
{
|
|
GetObject()->m_pos = m_moveto_pos;
|
|
if ( m_moveto_node_num != -1 )
|
|
{
|
|
HitWaypoint( m_moveto_node_num );
|
|
}
|
|
return true;
|
|
}
|
|
Mth::Vector newPos = m_moveto_pos - m_orig_pos;
|
|
newPos *= ( m_moveto_dist_traveled / m_moveto_dist );
|
|
GetObject()->m_pos = m_orig_pos + newPos;
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::OrientToNode(Script::CStruct* pNodeData)
|
|
{
|
|
Mth::Vector rot;
|
|
GetObject()->m_matrix.Ident( ); // Set identity before we decided if we need to rotate or not, in case we don't.
|
|
SkateScript::GetAngles( pNodeData, &rot );
|
|
if ( rot[ X ] || rot[ Y ] || rot[ Z ] )
|
|
{
|
|
// AML: This was rotating Z, then X, then Y
|
|
// See me if there are problems, and I'll change
|
|
// the exporter to do Z,X,Y
|
|
|
|
//m_matrix.RotateZ( rot[ Z ] );
|
|
GetObject()->GetMatrix().RotateX( rot[ X ] );
|
|
GetObject()->GetMatrix().RotateY( rot[ Y ] );
|
|
GetObject()->GetMatrix().RotateZ( rot[ Z ] );
|
|
}
|
|
//printf ("OrientToNode %d, (%f,%f,%f)\n",nodeNum,rot[X],rot[Y],rot[Z]);
|
|
|
|
// sync up the display matrix to the physics matrix
|
|
GetObject()->GetDisplayMatrix() = GetObject()->m_matrix;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::QuatRot( void )
|
|
{
|
|
float percent;
|
|
m_rot_time += m_time;
|
|
if ( ( !m_rot_time_target ) || ( m_rot_time >= m_rot_time_target ) )
|
|
{
|
|
percent = 1.0f;
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_QUAT_ROT;
|
|
}
|
|
else
|
|
{
|
|
percent = m_rot_time / m_rot_time_target;
|
|
}
|
|
|
|
Dbg_MsgAssert( mp_slerp,( "Missing mp_slerp..." ));
|
|
mp_slerp->getMatrix( &GetObject()->m_matrix, percent );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::FollowPath( void )
|
|
{
|
|
// temp hack to separate new peds from old path ob stuff
|
|
Obj::CPedLogicComponent* pPedLogicComp = GetPedLogicComponentFromObject( GetObject() );
|
|
if ( pPedLogicComp )
|
|
return;
|
|
|
|
Dbg_MsgAssert( GetPathOb(),( "Shouldn't be in FOLLOW_WAYPOINTS state without pathob." ));
|
|
|
|
Mth::Vector movement = -GetObject()->GetPos();
|
|
|
|
// change velocity:
|
|
DoPathPhysics();
|
|
|
|
// do path traversal:
|
|
if ( m_vel_z )
|
|
{
|
|
/*
|
|
Mth::Vector d=GetPathOb()->m_wp_pos_to-GetPathOb()->m_wp_pos_from;
|
|
m_shadow_normal.Set(0.0f,1.0f,0.0f);
|
|
m_shadow_normal=Mth::CrossProduct(m_shadow_normal,d);
|
|
m_shadow_normal=Mth::CrossProduct(m_shadow_normal,d);
|
|
m_shadow_normal.Normalize();
|
|
*/
|
|
|
|
// send in our forward direction, distance traveled...
|
|
//if ( GetPathOb()->TraversePath( is_being_skitched, m_vel_z * m_time,
|
|
// ( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) ? NULL : &m_matrix[ Z ] ) )
|
|
if ( GetPathOb()->TraversePath( m_vel_z * m_time,
|
|
( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) ? NULL : &GetObject()->m_matrix[ Z ] ) )
|
|
{
|
|
// path is done... turn off flag:
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_ON_PATH;
|
|
m_vel_z = 0.0f;
|
|
}
|
|
|
|
if ( !( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) )
|
|
{
|
|
// set up the matrix according to the nav point heading...
|
|
GetPathOb()->SetHeading( &GetObject()->m_matrix[ Z ] );
|
|
if ( m_movingobj_flags & MOVINGOBJ_FLAG_NO_PITCH_PLEASE )
|
|
{
|
|
if ( ( GetObject()->m_matrix[ Z ] )[ Y ] )
|
|
{
|
|
( GetObject()->m_matrix[ Z ] )[ Y ] = 0.0f;
|
|
GetObject()->m_matrix[ Z ].Normalize( );
|
|
}
|
|
}
|
|
// this heading may be changed in a moment, when we stick
|
|
// the object to the ground... if it isn't stuck, we should
|
|
// normalize the matrix around that heading...
|
|
if ( !( m_movingobj_flags & MOVINGOBJ_FLAG_STICK_TO_GROUND ) )
|
|
{
|
|
// make sure our matrix is orthagonal:
|
|
GetObject()->m_matrix[ X ] = Mth::CrossProduct( GetObject()->m_matrix[ Y ], GetObject()->m_matrix[ Z ]);
|
|
GetObject()->m_matrix[ X ].Normalize();
|
|
// This will work for things whose Y axis is always up...
|
|
// May have to add flags for things that rotate along Z axis or
|
|
// far along X axis:
|
|
GetObject()->m_matrix[ Z ] = Mth::CrossProduct( GetObject()->m_matrix[ X ], GetObject()->m_matrix[ Y ] );
|
|
GetObject()->m_matrix[ Z ].Normalize( );
|
|
}
|
|
}
|
|
if ( m_movingobj_flags & MOVINGOBJ_FLAG_STICK_TO_GROUND )
|
|
{
|
|
// Just move it to the nav pos ( except for y axis ) then stick it to the ground...
|
|
float origY = GetObject()->m_pos[ Y ];
|
|
GetObject()->m_pos = GetPathOb()->m_nav_pos;
|
|
GetObject()->m_pos[ Y ] = origY;
|
|
|
|
StickToGround();
|
|
}
|
|
else
|
|
{
|
|
float origY = GetObject()->m_pos[ Y ];
|
|
GetObject()->m_pos = GetPathOb()->m_nav_pos;
|
|
if ( m_movingobj_flags & MOVINGOBJ_FLAG_CONSTANT_HEIGHT )
|
|
{
|
|
GetObject()->m_pos[ Y ] = origY;
|
|
}
|
|
}
|
|
}
|
|
|
|
movement += GetObject()->m_pos; // actually calculate movement
|
|
GetObject()->m_vel = movement / m_time; // calculate the velocity, so we can impart velocity in things we hit, etc
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// call SetUpLookAtPos first, or one of the other rotate
|
|
// setup functions, then call this until it returns true:
|
|
bool CMotionComponent::Rotate( int rotAxis )
|
|
{
|
|
|
|
bool done = false;
|
|
|
|
// update angular acceleration/angular velocity:
|
|
if ( m_delta_turn_speed[ rotAxis ] )
|
|
{
|
|
m_turn_speed[ rotAxis ] += m_delta_turn_speed[ rotAxis ] * m_time;
|
|
if ( ( Mth::Abs( m_turn_speed[ rotAxis ] ) > Mth::Abs( m_turn_speed_target[ rotAxis ] ) )
|
|
&& ( ( m_turn_speed[ rotAxis ] > 0.0f ) == ( m_turn_speed_target[ rotAxis ] > 0.0f ) ) )
|
|
{
|
|
// stop angular acceleration...settle on the target:
|
|
m_turn_speed[ rotAxis ] = m_turn_speed_target[ rotAxis ];
|
|
}
|
|
}
|
|
|
|
if ( m_angular_friction[ rotAxis ] )
|
|
{
|
|
float friction = m_angular_friction[ rotAxis ] * m_time;
|
|
// Ken: Changed this so that it decelerates down to the m_turn_speed_target rather than 0
|
|
if ( Mth::Abs( friction ) > Mth::Abs( m_turn_speed[ rotAxis ] - m_turn_speed_target[ rotAxis ] ) )
|
|
{
|
|
// Settle on the target speed
|
|
m_turn_speed[ rotAxis ] = m_turn_speed_target[ rotAxis ];
|
|
// Switch off the friction so that it deos not slow down any more
|
|
m_angular_friction[ rotAxis ] = 0.0f;
|
|
|
|
Dbg_Message( "done rotating due to deceleration... %s angle traversed = %f",
|
|
rotAxis == X ? "X" : ( rotAxis == Y ? "Y" : "Z" ), RADIANS_TO_DEGREES( m_cur_turn_ang[ rotAxis ] ) );
|
|
if (m_turn_speed_target[ rotAxis ]==0.0f)
|
|
{
|
|
return true; // done rotating...
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_turn_speed[ rotAxis ] -= friction;
|
|
}
|
|
}
|
|
float cur_turn_dist = m_turn_speed[ rotAxis ] * m_time;
|
|
|
|
if ( !( m_movingobj_flags & ( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis ) ) )
|
|
{
|
|
bool positive = m_turn_ang_target[ rotAxis ] > m_cur_turn_ang[ rotAxis ];
|
|
m_cur_turn_ang[ rotAxis ] += cur_turn_dist;
|
|
if ( positive != ( m_turn_ang_target[ rotAxis ] > m_cur_turn_ang[ rotAxis ] ) )
|
|
{
|
|
//done rotating!
|
|
done = true;
|
|
// and deltaYRot should be altered so as to hit 0 exactly:
|
|
cur_turn_dist += m_turn_ang_target[ rotAxis ] - m_cur_turn_ang[ rotAxis ];
|
|
// m_cur_turn_ang[ rotAxis ] = m_turn_ang_target[ rotAxis ];
|
|
}
|
|
}
|
|
|
|
Rot( rotAxis, cur_turn_dist );
|
|
|
|
return ( done );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::RotUpdate( void )
|
|
{
|
|
// rotate states:
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
if ( m_movingobj_status & ( MOVINGOBJ_STATUS_ROTX << i ) )
|
|
{
|
|
if ( Rotate( i ) )
|
|
{
|
|
m_movingobj_status &= ~( MOVINGOBJ_STATUS_ROTX << i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
|
|
static bool s_do_collision_check(const Mth::Vector *p_v0, const Mth::Vector *p_v1, Mth::Vector *p_collisionPoint,
|
|
STriangle *p_lastTriangle)
|
|
{
|
|
// First, see if there is a collision with the last triangle, and if there is, use that.
|
|
Mth::Vector d=*p_v1-*p_v0;
|
|
float alpha=0.0f;
|
|
if (Nx::CCollObj::sRayTriangleCollision(p_v0, &d,
|
|
&p_lastTriangle->mpVertices[0],
|
|
&p_lastTriangle->mpVertices[1],
|
|
&p_lastTriangle->mpVertices[2], &alpha))
|
|
{
|
|
*p_collisionPoint = *p_v0+d*alpha;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
CFeeler feeler;
|
|
if (feeler.GetCollision(*p_v0,*p_v1, false))
|
|
{
|
|
Nx::CCollObj* p_col_obj=feeler.GetSector();
|
|
Dbg_MsgAssert(p_col_obj->IsTriangleCollision(),("Not triangle collision !!!"));
|
|
|
|
Nx::CCollObjTriData* p_tri_data=p_col_obj->GetGeometry();
|
|
Dbg_MsgAssert(p_tri_data,("NULL p_tri_data"));
|
|
|
|
int face_index=feeler.GetFaceIndex();
|
|
p_lastTriangle->mpVertices[0]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 0));
|
|
p_lastTriangle->mpVertices[1]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 1));
|
|
p_lastTriangle->mpVertices[2]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 2));
|
|
|
|
// if there is a collision, then snap to it
|
|
*p_collisionPoint = feeler.GetPoint();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CMotionComponent::StickToGround()
|
|
{
|
|
if ( ( !m_col_dist_above ) && ( !m_col_dist_below ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( m_point_stick_to_ground )
|
|
{
|
|
// standard stick to ground
|
|
// (also need a car version,
|
|
// which will be a separate component)
|
|
|
|
Mth::Vector v0, v1;
|
|
|
|
// make sure our X vector is correct (got Z from the nav matrix...)
|
|
GetObject()->m_matrix[ X ] = Mth::CrossProduct( GetObject()->m_matrix[ Y ], GetObject()->m_matrix[ Z ]);
|
|
GetObject()->m_matrix[ X ].Normalize();
|
|
|
|
v0.Set( 0.0f, FEET_TO_INCHES( fabs( m_col_dist_above ) ), 0.0f );
|
|
v1.Set( 0.0f, -FEET_TO_INCHES( fabs( m_col_dist_below ) ), 0.0f );
|
|
|
|
v0 += GetObject()->m_pos;
|
|
v1 += GetObject()->m_pos;
|
|
|
|
// get disatnce to ground
|
|
// and snap the skater to it
|
|
|
|
Mth::Vector collision_point;
|
|
if (s_do_collision_check(&v0, &v1, &collision_point, &m_last_triangle))
|
|
{
|
|
GetObject()->m_pos=collision_point;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// if it's a car, then it's big
|
|
Mth::Vector v0, v1, vectForward, vectLeft, leftCollidePoint, rightCollidePoint;
|
|
bool leftCollision = false;
|
|
bool rightCollision = false;
|
|
CFeeler feeler;
|
|
|
|
GetObject()->m_matrix[ Y ].Set( 0.0f, 1.0f, 0.0f );
|
|
// make sure our X vector is correct (got Y and Z from the nav matrix...)
|
|
GetObject()->m_matrix[ X ] = Mth::CrossProduct( GetObject()->m_matrix[ Y ], GetObject()->m_matrix[ Z ]);
|
|
GetObject()->m_matrix[ X ].Normalize();
|
|
|
|
vectForward = GetObject()->m_matrix[ Z ] * FEET_TO_INCHES( 6.0f );
|
|
vectLeft = GetObject()->m_matrix[ X ] * FEET_TO_INCHES( 2.5f );
|
|
|
|
GetObject()->m_pos -= vectForward;
|
|
GetObject()->m_pos += vectLeft;
|
|
|
|
v0 = v1 = GetObject()->m_pos;
|
|
v0[ Y ] += FEET_TO_INCHES( m_col_dist_above );
|
|
v1[ Y ] -= FEET_TO_INCHES( m_col_dist_below );
|
|
|
|
// find a collision point under the left rear:
|
|
if (s_do_collision_check(&v0, &v1, &leftCollidePoint, &m_car_left_rear_last_triangle))
|
|
{
|
|
leftCollision = true;
|
|
}
|
|
|
|
GetObject()->m_pos -= vectLeft * 2;
|
|
|
|
v0 = v1 = GetObject()->m_pos;
|
|
v0[ Y ] += FEET_TO_INCHES( m_col_dist_above );
|
|
v1[ Y ] -= FEET_TO_INCHES( m_col_dist_below );
|
|
|
|
// find a collision point under the right rear:
|
|
if (s_do_collision_check(&v0, &v1, &rightCollidePoint, &m_car_right_rear_last_triangle))
|
|
{
|
|
rightCollision = true;
|
|
if ( leftCollision )
|
|
{
|
|
GetObject()->m_matrix[ X ] = leftCollidePoint - rightCollidePoint;
|
|
GetObject()->m_matrix[ X ].Normalize( );
|
|
}
|
|
}
|
|
|
|
GetObject()->m_pos += vectForward * 2;
|
|
GetObject()->m_pos += vectLeft;
|
|
|
|
v0 = v1 = GetObject()->m_pos;
|
|
v0[ Y ] += FEET_TO_INCHES( m_col_dist_above );
|
|
v1[ Y ] -= FEET_TO_INCHES( m_col_dist_below );
|
|
|
|
GetObject()->m_pos -= vectForward;
|
|
|
|
Mth::Vector mid_front_collide_point;
|
|
if (s_do_collision_check(&v0, &v1, &mid_front_collide_point, &m_car_mid_front_last_triangle))
|
|
{
|
|
if ( rightCollision && leftCollision )
|
|
{
|
|
GetObject()->m_pos[ Y ] = leftCollidePoint[ Y ] + rightCollidePoint[ Y ];
|
|
GetObject()->m_pos[ Y ] /= 2;
|
|
GetObject()->m_pos[ Y ] += mid_front_collide_point.GetY();
|
|
GetObject()->m_pos[ Y ] /= 2;
|
|
|
|
Mth::Vector heading;
|
|
|
|
heading = leftCollidePoint + rightCollidePoint;
|
|
heading /= 2;
|
|
heading -= mid_front_collide_point;
|
|
heading *= -1;
|
|
heading.Normalize( );
|
|
|
|
GetObject()->m_matrix[ Z ] += heading;
|
|
GetObject()->m_matrix[ Z ] /= 2;
|
|
|
|
GetObject()->m_matrix[ Y ] = Mth::CrossProduct( GetObject()->m_matrix[ Z ], GetObject()->m_matrix[ X ] );
|
|
GetObject()->m_matrix[ Y ].Normalize( );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// (Mick) This assertion has been removed, as cars sometimes drive where the skater cannot go, and hence into no collision
|
|
// Dbg_MsgAssert( 0, ( "StickToGround car %s was driving on an area with no collision", Script::FindChecksumName(GetID()) ) );
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::GetDebugInfo( Script::CStruct* p_info )
|
|
{
|
|
#ifdef __DEBUG_CODE__
|
|
Dbg_MsgAssert( p_info, ( "NULL p_info sent to CMotionComponent::GetDebugInfo" ) );
|
|
CBaseComponent::GetDebugInfo(p_info);
|
|
|
|
p_info->AddChecksum( CRCD(0x9e6b250,"m_movingobj_status"), m_movingobj_status );
|
|
p_info->AddChecksum( CRCD(0xe5dff85a,"m_movingobj_flags"), m_movingobj_flags );
|
|
p_info->AddFloat( CRCD(0xc2d3b58d,"m_moveto_speed"), m_moveto_speed );
|
|
p_info->AddFloat( CRCD(0x3a3ae81a,"m_acceleration"), m_acceleration );
|
|
p_info->AddFloat( CRCD(0x4a2c5549,"m_deceleration"), m_deceleration );
|
|
p_info->AddFloat( CRCD(0x472b8f9b,"m_max_vel"), m_max_vel );
|
|
p_info->AddFloat( CRCD(0xc37e4e25,"m_vel_z"), m_vel_z );
|
|
p_info->AddFloat( CRCD(0xcab44eaf,"m_stop_dist"), m_stop_dist );
|
|
|
|
// p_info->AddFloat( "m_col_dist_above", m_col_dist_above );
|
|
// p_info->AddFloat( "m_col_dist_below", m_col_dist_below );
|
|
// p_info->AddVector( "m_last_triangle_v0", m_last_triangle_v0 );
|
|
// p_info->AddVector( "m_last_triangle_v1", m_last_triangle_v1 );
|
|
// p_info->AddVector( "m_last_triangle_v2", m_last_triangle_v2 );
|
|
// p_info->AddVector( "m_last_normal", m_last_normal );
|
|
p_info->AddInteger( CRCD(0xa13363b0,"m_point_stick_to_ground"), m_point_stick_to_ground );
|
|
|
|
p_info->AddFloat( CRCD(0xac5b4824,"m_moveto_dist"), m_moveto_dist );
|
|
p_info->AddFloat( CRCD(0x3e3570a6,"m_moveto_dist_traveled"), m_moveto_dist_traveled );
|
|
p_info->AddFloat( CRCD(0x93e73df1,"m_moveto_speed_target"), m_moveto_speed_target );
|
|
p_info->AddFloat( CRCD(0x6819fd8d,"m_moveto_acceleration"), m_moveto_acceleration );
|
|
|
|
p_info->AddInteger( CRCD(0xd6d94326,"m_moveto_node_num"), m_moveto_node_num );
|
|
|
|
// p_info->AddVector( "m_turn_ang_target", m_turn_ang_target );
|
|
// p_info->AddVector( "m_cur_turn_ang", m_cur_turn_ang );
|
|
// p_info->AddVector( "m_turn_speed_target", m_turn_speed_target );
|
|
p_info->AddVector( CRCD(0x9d27cc70,"m_turn_speed"), m_turn_speed );
|
|
// p_info->AddVector( "m_delta_turn_speed", m_delta_turn_speed );
|
|
// p_info->AddVector( "m_angular_friction", m_angular_friction );
|
|
p_info->AddVector( CRCD(0xfa4307e,"m_orig_pos"), m_orig_pos );
|
|
|
|
// p_info->AddFloat( "m_stop_dist_traveled", m_stop_dist_traveled );
|
|
// p_info->AddFloat( "m_vel_z_start", m_vel_z_start );
|
|
// p_info->AddFloat( "m_min_stop_vel", m_min_stop_vel );
|
|
|
|
p_info->AddVector( CRCD(0xc3e1cded,"m_stored_pos"), m_stored_pos );
|
|
p_info->AddInteger( CRCD(0x56e54ee5,"m_stored_node"), m_stored_node );
|
|
|
|
// CPathObjectTracker* mp_path_object_tracker;
|
|
|
|
// Mth::SlerpInterpolator* mp_slerp;
|
|
// p_info->AddFloat( "m_rot_time_target", m_rot_time_target );
|
|
p_info->AddFloat( CRCD(0x622c6c9b,"m_rot_time"), m_rot_time );
|
|
|
|
p_info->AddInteger( CRCD(0x80dd9065,"m_start_node"), m_start_node );
|
|
p_info->AddInteger( CRCD(0xa80dc42,"m_current_node"), m_current_node );
|
|
|
|
if (mp_pathOb)
|
|
{
|
|
Script::CStruct* p_pathOb_params = new Script::CStruct();
|
|
mp_pathOb->GetDebugInfo( p_pathOb_params );
|
|
p_info->AddStructure( CRCD(0x81270c1f,"mp_pathOb"), p_pathOb_params );
|
|
delete p_pathOb_params;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CCompositeObject* CMotionComponent::GetNextObjectOnPath( float range )
|
|
{
|
|
CCompositeObject* p_closest_ob=NULL;
|
|
|
|
if ( mp_path_object_tracker )
|
|
{
|
|
const CSmtPtr<CCompositeObject>* pp_object_list = mp_path_object_tracker->GetObjectList();
|
|
|
|
float min_dist = 0.0f;
|
|
for (int i=0; i<CPathObjectTracker::MAX_OBJECTS_PER_PATH; ++i)
|
|
{
|
|
if (pp_object_list[i])
|
|
{
|
|
CCompositeObject* p_ob=pp_object_list[i].Convert();
|
|
|
|
if (p_ob!=GetObject() &&
|
|
Mth::DotProduct(GetObject()->m_matrix[Z],p_ob->GetMatrix()[Z]) >= 0.0f &&
|
|
Mth::DotProduct(GetObject()->m_matrix[Z],p_ob->GetPos()-GetObject()->GetPos()) >= 0.0f
|
|
)
|
|
{
|
|
if (GetMotionComponentFromObject(p_ob)->GetDestinationPathNode()==GetDestinationPathNode() &&
|
|
GetMotionComponentFromObject(p_ob)->GetPreviousPathNode()!=GetPreviousPathNode())
|
|
{
|
|
// Make vehicles on converging paths not collide, otherwise
|
|
// a pile up happens at the start of the car paths in the school
|
|
// Note: Actually, this didn't fix it ... might be a script bug?
|
|
}
|
|
else
|
|
{
|
|
float d=Mth::Distance( GetObject()->GetPos(), p_ob->GetPos());
|
|
if( d <= range )
|
|
{
|
|
if( !p_closest_ob || d < min_dist )
|
|
{
|
|
min_dist = d;
|
|
p_closest_ob = p_ob;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return p_closest_ob;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
|
|
void CMotionComponent::FollowLeader( void )
|
|
{
|
|
Dbg_MsgAssert( mp_follow_ob,( "Shouldn't be in follow-leader state without mp_follow_ob." ));
|
|
|
|
mp_follow_ob->GetNewPathPointFromObjectBeingFollowed();
|
|
|
|
float old_y = GetObject()->m_pos[Y];
|
|
mp_follow_ob->CalculatePositionAndOrientation(&GetObject()->m_pos,&GetObject()->m_matrix);
|
|
if (m_leave_y_unaffected_when_following_leader)
|
|
{
|
|
GetObject()->m_pos[Y]=old_y;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::FollowLeader_Init( Script::CStruct* pParams )
|
|
{
|
|
if (pParams->ContainsFlag(CRCD(0xd443a2bc,"Off")))
|
|
{
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_FOLLOWING_LEADER;
|
|
if (mp_follow_ob)
|
|
{
|
|
delete mp_follow_ob;
|
|
mp_follow_ob=NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
m_leave_y_unaffected_when_following_leader=pParams->ContainsFlag("LeaveYUnaffected");
|
|
|
|
m_movingobj_status |= MOVINGOBJ_STATUS_FOLLOWING_LEADER;
|
|
|
|
// these can't coexist:
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
|
|
|
|
// GetMotionComponent()->m_movingobj_status &= ~MOVINGOBJ_STATUS_LOCKED_TO_OBJECT;
|
|
Obj::CLockObjComponent* pLockObjComponent = GetLockObjComponentFromObject(GetObject());
|
|
pLockObjComponent->EnableLock( false );
|
|
|
|
if ( !mp_follow_ob )
|
|
{
|
|
mp_follow_ob = new CFollowOb;
|
|
}
|
|
mp_follow_ob->Reset();
|
|
|
|
float distance=0.0f;
|
|
pParams->GetFloat(CRCD(0xe36d657e,"Distance"),&distance);
|
|
mp_follow_ob->SetDistance(distance);
|
|
|
|
uint32 name=0;
|
|
pParams->GetChecksum(CRCD(0xa1dc81f9,"Name"),&name);
|
|
mp_follow_ob->SetLeaderName(name);
|
|
|
|
if (pParams->ContainsFlag(CRCD(0xe19e310a,"OrientY")))
|
|
{
|
|
mp_follow_ob->OrientY();
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CMotionComponent::DoJump( void )
|
|
{
|
|
Mth::Vector new_pos;
|
|
|
|
// Gfx::AddDebugLine( m_jump_start_pos, m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
|
|
float distance = m_jump_speed * m_time + ( m_jump_gravity * 0.5f * m_time * m_time );
|
|
// printf("m_jump_speed = %f, m_jump_gravity = %f\n, m_jump_original_speed = %f, m_jump_heading = (%f, %f, %f)\n", m_jump_speed, m_jump_gravity, m_jump_original_speed, m_jump_heading[X], m_jump_heading[Y], m_jump_heading[Z]);
|
|
new_pos = GetObject()->m_pos;
|
|
if ( m_jump_use_heading )
|
|
{
|
|
// first figure the pos if we stayed on our original heading
|
|
new_pos += ( m_jump_heading * m_jump_original_speed * m_time );
|
|
new_pos[Y] = GetObject()->m_pos[Y];
|
|
}
|
|
new_pos[Y] += distance;
|
|
|
|
m_jump_speed += m_jump_gravity * m_time;
|
|
|
|
// do land checks if we're on our way down
|
|
if ( distance < 0.0f )
|
|
{
|
|
if ( m_jump_use_land_height )
|
|
{
|
|
if ( new_pos[Y] < m_jump_land_height )
|
|
{
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
|
|
new_pos[Y] = m_jump_land_height;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// use collision checks
|
|
CFeeler feeler;
|
|
feeler.m_start = new_pos + Mth::Vector( 0, m_jump_col_dist_above, 0 );
|
|
feeler.m_end = new_pos + Mth::Vector( 0, -m_jump_col_dist_below, 0 );
|
|
if ( feeler.GetCollision() )
|
|
{
|
|
m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
|
|
if ( m_jump_start_pos[X] != GetObject()->m_pos[X] || m_jump_start_pos[Z] != GetObject()->m_pos[Z] )
|
|
{
|
|
// printf("StickToGround %s\n",Script::FindChecksumName(GetID()));
|
|
// GetMotionComponent()->StickToGround();
|
|
// m_jump_start_pos = m_pos;
|
|
|
|
/*if ( new_pos[Y] < m_jump_start_pos[Y])
|
|
{
|
|
new_pos[Y] = m_jump_start_pos[Y];
|
|
GetMotionComponent()->m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
|
|
}*/
|
|
}
|
|
else
|
|
{
|
|
// new_pos[Y] = m_jump_start_pos[Y];
|
|
// GetMotionComponent()->m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_jump_pos = new_pos;
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|