mirror of
https://github.com/thug1src/thug.git
synced 2024-11-30 12:06:44 +00:00
1097 lines
35 KiB
C++
1097 lines
35 KiB
C++
//****************************************************************************
|
|
//* MODULE: Gel/Object
|
|
//* FILENAME: compositeobject.cpp
|
|
//* OWNER: Mick West
|
|
//* CREATION DATE: 10/17/2002
|
|
//****************************************************************************
|
|
|
|
/***************************************************************************
|
|
A composite object is a generic object which basically consists of:
|
|
- a position and orientation
|
|
- some flags
|
|
- a script
|
|
- a list of components
|
|
|
|
The purpose of the Composite Object is to represent a thing that exists
|
|
in the game world. For example:
|
|
|
|
- Cars and other vechicals
|
|
- Pedestrians, animals, etc
|
|
- Pickup Icons
|
|
- 3D sound emitters
|
|
- Particle systems
|
|
- Skaters or other player type objects
|
|
- Bouncy Objects like trash cans
|
|
|
|
CCompositeObject is derived from CObject, so all CCompositeObjects have
|
|
a mp_script (which might be NULL)
|
|
|
|
A composite object is normally comprised of several components. These are
|
|
generally independent things such as:
|
|
- A Model componet
|
|
- A physics component (e.g., drive around, or skating)
|
|
- A suspend component
|
|
- and various other components as needed
|
|
|
|
There is one global list of all composite objects. This is managed by the
|
|
CCompositeObjectManager class, whcih is also responsible for creating
|
|
the composite objects.
|
|
|
|
A composite object is typically create from an array of structs
|
|
and a structe that contains the initialization parameters.
|
|
|
|
Here's an example of the array of structs. Note that some components
|
|
have default parameters. These can be overridden by the struct of parameters
|
|
|
|
gameobj_composite_structure = [
|
|
{ component = suspend }
|
|
{ component = model }
|
|
{ component = exception }
|
|
{ component = collision }
|
|
]
|
|
|
|
****************************************************************************/
|
|
|
|
#include <gel/object/compositeobject.h>
|
|
#include <gel/object/compositeobjectManager.h>
|
|
#include <gel/scripting/script.h>
|
|
#include <gel/scripting/struct.h>
|
|
#include <gel/scripting/array.h>
|
|
#include <gel/scripting/utils.h>
|
|
#include <gel/scripting/vecpair.h>
|
|
#include <gel/scripting/checksum.h>
|
|
#include <modules/skate/skate.h>
|
|
#include <sk/scripting/nodearray.h> // Needed by GetDebugInfo
|
|
#include <sk/engine/feeler.h>
|
|
|
|
|
|
namespace Obj
|
|
{
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CCompositeObject::CCompositeObject()
|
|
{
|
|
// set position and orientation to a known state (at the origin, identity matrix)
|
|
m_vel.Set( 0.0f, 0.0f, 0.0f );
|
|
m_pos.Set( 0.0f, 0.0f, 0.0f );
|
|
m_matrix.Ident();
|
|
m_display_matrix.Ident();
|
|
|
|
mp_component_list = NULL;
|
|
m_composite_object_flags.ClearAll();
|
|
|
|
SetFlags( GetFlags() | vCOMPOSITE); // Kind of a temp solution for now
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CCompositeObject::~CCompositeObject()
|
|
{
|
|
while ( mp_component_list )
|
|
{
|
|
// Get the component at the head of the list
|
|
CBaseComponent* pComponent = mp_component_list;
|
|
|
|
// advance the list past this component, effectivly isolating it
|
|
// (unless someone else is storing a pointer to it)
|
|
mp_component_list = pComponent->mp_next;
|
|
|
|
// remove the component from the by-type list of components maintained by the CompositeObjectManager
|
|
Obj::CCompositeObjectManager::Instance()->RemoveComponentByType( pComponent );
|
|
|
|
// delete the isolated component
|
|
delete pComponent;
|
|
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
// Runs through all the components and zeroes their update times.
|
|
void CCompositeObject::zero_component_update_times()
|
|
{
|
|
CBaseComponent* pComponent = mp_component_list;
|
|
while ( pComponent )
|
|
{
|
|
pComponent->m_update_time=0;
|
|
pComponent = pComponent->mp_next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CCompositeObject::Update()
|
|
{
|
|
#ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PushContext( m_profile_color );
|
|
#endif // __USE_PROFILER__
|
|
|
|
Dbg_MsgAssert(IsFinalized(),("Update called on UnFinalized Composite object %s",Script::FindChecksumName(GetID())));
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
// GJ: don't do this if the composite object is paused,
|
|
// because some lock obj component might be trying
|
|
// to lock to this object while the game is paused
|
|
if (!m_composite_object_flags.Test(CO_SUSPENDED)
|
|
&& !m_composite_object_flags.Test(CO_PAUSED))
|
|
{
|
|
// clear out the display matrix, to make
|
|
// sure that no one's relying on the
|
|
// last frame's matrix
|
|
m_display_matrix.Ident();
|
|
}
|
|
#endif
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
// Make sure the component update times are initialized to zero so that they do not
|
|
// show incorrect old values in the debugger if this function returns before
|
|
// getting to the component update loop.
|
|
zero_component_update_times();
|
|
|
|
m_total_script_update_time=0;
|
|
m_do_game_logic_time=0;
|
|
#endif
|
|
|
|
if (m_composite_object_flags.Test(CO_PAUSED))
|
|
{
|
|
#ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PopContext();
|
|
#endif // __USE_PROFILER__
|
|
return;
|
|
}
|
|
|
|
|
|
if (!m_composite_object_flags.Test(CO_SUSPENDED))
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
Tmr::CPUCycles time_before=Tmr::GetTimeInCPUCycles();
|
|
#endif
|
|
if ( mp_script )
|
|
{
|
|
if ( mp_script->Update() == Script::ESCRIPTRETURNVAL_FINISHED )
|
|
{
|
|
// if we have script based events then we only want to kill the script if
|
|
// it has an empty event handler table
|
|
// as a script can finish, but still have event handlers
|
|
// this kind of logic will be duplicated in various places in the code
|
|
// so we could do with a bit of refactoring
|
|
// like a mp_script->CanBeDeleted() function
|
|
#ifdef __SCRIPT_EVENT_TABLE__
|
|
// in practice it seems okay to just leave the script
|
|
// probably not a big issue
|
|
#else
|
|
delete mp_script;
|
|
mp_script = NULL;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// if a script has called "Die",
|
|
// then don't update the components
|
|
if ( IsDead() )
|
|
{
|
|
# ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PopContext();
|
|
# endif // __USE_PROFILER__
|
|
return;
|
|
}
|
|
#ifdef __NOPT_ASSERT__
|
|
// Convert to microseconds by dividing by 150
|
|
m_total_script_update_time=(Tmr::GetTimeInCPUCycles()-time_before)/150;
|
|
#endif
|
|
|
|
// transition-only function call,
|
|
// call each specific object's
|
|
// DoGameLogic() function (previously,
|
|
// this was called by each object's task)
|
|
#ifdef __NOPT_ASSERT__
|
|
time_before=Tmr::GetTimeInCPUCycles();
|
|
#endif
|
|
// Mick: DoGameLogic is Deprecated, and only exists for a few misc objects
|
|
// it should eventually be removed
|
|
DoGameLogic();
|
|
#ifdef __NOPT_ASSERT__
|
|
// Convert to microseconds by dividing by 150
|
|
m_do_game_logic_time=(Tmr::GetTimeInCPUCycles()-time_before)/150;
|
|
#endif
|
|
|
|
}
|
|
|
|
CBaseComponent* pComponent = mp_component_list;
|
|
while ( pComponent )
|
|
{
|
|
if ( ! (pComponent->m_flags.Test(CBaseComponent::BC_NO_UPDATE)))
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
Tmr::CPUCycles time_before_components=Tmr::GetTimeInCPUCycles();
|
|
#endif
|
|
|
|
pComponent->Update();
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
// Convert to microseconds by dividing by 150
|
|
pComponent->m_update_time=(Tmr::GetTimeInCPUCycles()-time_before_components)/150;
|
|
#endif
|
|
|
|
// If a component update has killed the object
|
|
// then we don't process any more components
|
|
// as they might attempt to fire an event, or reference this object in some way
|
|
// and it won't be in the tracking system any more
|
|
if (IsDead())
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
pComponent = pComponent->mp_next;
|
|
}
|
|
# ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PopContext();
|
|
# endif // __USE_PROFILER__
|
|
|
|
m_composite_object_flags.Clear(CO_TELEPORTED);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CCompositeObject::Hide( bool should_hide )
|
|
{
|
|
if ( should_hide == m_composite_object_flags.Test(CO_HIDDEN) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_composite_object_flags.Set(CO_HIDDEN, should_hide);
|
|
|
|
// loop through all the components
|
|
// and call their individual Hide functions...
|
|
CBaseComponent* pComponent = mp_component_list;
|
|
while ( pComponent )
|
|
{
|
|
pComponent->Hide( should_hide );
|
|
pComponent = pComponent->mp_next;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Suspending a composite object tells it not to update the script
|
|
// but the individual components take care of suspending themselves
|
|
// (Generally handled by the BC_NO_UPDATE flag being set)
|
|
// "suspending" is generally soemthing done when the object goes a
|
|
// certain distance from the camera
|
|
void CCompositeObject::Suspend(bool suspended)
|
|
{
|
|
if (suspended == m_composite_object_flags.Test(CO_SUSPENDED)) return;
|
|
|
|
m_composite_object_flags.Set(CO_SUSPENDED, suspended);
|
|
|
|
// suspend the individual components
|
|
CBaseComponent* pComponent = mp_component_list;
|
|
while ( pComponent )
|
|
{
|
|
pComponent->Suspend(suspended);
|
|
pComponent = pComponent->mp_next;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CCompositeObject::Teleport()
|
|
{
|
|
// calls each component's Teleport() function so
|
|
// that things like the model, the collision, and the
|
|
// shadow can be updated immediately when the
|
|
// object has radically changed position
|
|
CBaseComponent* pComponent = mp_component_list;
|
|
while ( pComponent )
|
|
{
|
|
pComponent->Teleport();
|
|
pComponent = pComponent->mp_next;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CCompositeObject::AddComponent( CBaseComponent* pComponent )
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
if ( GetComponent( pComponent->GetType() ) )
|
|
{
|
|
Dbg_MsgAssert( 0, ( "Object already has a component of type '%s'", Script::FindChecksumName(pComponent->GetType()) ) );
|
|
}
|
|
#endif
|
|
|
|
Dbg_MsgAssert(!IsFinalized(),("Adding Component %s to Finalized Composite object %s",Script::FindChecksumName(pComponent->GetType()),Script::FindChecksumName(GetID())));
|
|
|
|
if (!mp_component_list)
|
|
{
|
|
mp_component_list = pComponent;
|
|
}
|
|
else
|
|
{
|
|
CBaseComponent* p_tail = mp_component_list;
|
|
while(p_tail->mp_next)
|
|
{
|
|
p_tail = p_tail->mp_next;
|
|
}
|
|
p_tail->mp_next = pComponent;
|
|
}
|
|
|
|
// now that the component is "officially" associated with
|
|
// this object, we can set the component's object ptr
|
|
pComponent->SetObject( this );
|
|
|
|
// add the component to the by-type list of components maintained by the CompositeObjectManager
|
|
Obj::CCompositeObjectManager::Instance()->AddComponentByType( pComponent );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Given a single component definition in the form:
|
|
// {
|
|
// component = ...
|
|
// .... other params
|
|
// }
|
|
//
|
|
// Then create a component of the correct type
|
|
// add it to this object
|
|
// and initialize it with this structure
|
|
//
|
|
// Optionally supply a seperate additional p_params structure from which to initialize it
|
|
|
|
void CCompositeObject::CreateComponentFromStructure(Script::CStruct *p_struct, Script::CStruct *p_params)
|
|
{
|
|
uint32 component_name;
|
|
p_struct->GetChecksum("component",&component_name,Script::ASSERT);
|
|
Obj::CBaseComponent* p_component = Obj::CCompositeObjectManager::Instance()->CreateComponent(component_name);
|
|
AddComponent(p_component); // Add it first, as InitFromStructure might need to query the object
|
|
if (p_params)
|
|
{
|
|
// If we have additional parameters, then add then in to the parameters in the array
|
|
// this will override the array paramaeters, which are assumed to be defaults
|
|
Script::CScriptStructure *p_combinedParams=new Script::CScriptStructure;
|
|
*p_combinedParams += *p_struct;
|
|
*p_combinedParams += *p_params;
|
|
p_component->InitFromStructure(p_combinedParams);
|
|
delete p_combinedParams;
|
|
}
|
|
else
|
|
{
|
|
// No additional parameters, so just initialze from the params in the structure in the array
|
|
p_component->InitFromStructure(p_struct);
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Given an array of component definitions in the form:
|
|
// [
|
|
// { component = ......
|
|
// ... other params .....
|
|
// }
|
|
// {
|
|
// ... other components
|
|
// }
|
|
// ]
|
|
// then iterate over the array, and add the components to the composite object
|
|
//
|
|
// Optionally supply a "p_params" structure that contains all the initialization info
|
|
// instead of interleaving it in the array
|
|
|
|
void CCompositeObject::CreateComponentsFromArray(Script::CArray *p_array, Script::CStruct* p_params)
|
|
{
|
|
int num_components = p_array->GetSize();
|
|
for (int i=0;i<num_components;i++)
|
|
{
|
|
Script::CStruct* p_struct = p_array->GetStructure(i);
|
|
CreateComponentFromStructure(p_struct, p_params);
|
|
}
|
|
}
|
|
|
|
|
|
// InitFromStructure is passed a Script::CStruct that contains a
|
|
// number of parameters to initialize this component
|
|
// this currently isfrequently the contents of a node
|
|
// but you can pass in anything you like.
|
|
void CCompositeObject::InitFromStructure( Script::CStruct* pParams )
|
|
{
|
|
uint32 NameChecksum;
|
|
if ( pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &NameChecksum ))
|
|
{
|
|
SetID(NameChecksum);
|
|
}
|
|
|
|
SkateScript::GetPosition(pParams,&m_pos);
|
|
|
|
SkateScript::GetOrientation(pParams,&m_matrix);
|
|
|
|
# ifdef __USE_PROFILER__
|
|
int ProfileColor = 0x800000; // default to blue profile color
|
|
pParams->GetInteger( CRCD(0x72444899,"ProfileColor"), &ProfileColor );
|
|
SetProfileColor(ProfileColor);
|
|
# endif // __USE_PROFILER__
|
|
|
|
if (pParams->ContainsFlag(CRCD(0x23627fd7,"permanent")))
|
|
{
|
|
SetFlags( GetFlags() | vLOCKED);
|
|
}
|
|
}
|
|
|
|
|
|
// Finalize is called after all components have been added
|
|
// and will call the virtual Finalize() function on each component
|
|
// This is intended for any components that are depended on other components
|
|
// but where the initialization order can't be guaranteed
|
|
void CCompositeObject::Finalize()
|
|
{
|
|
Dbg_MsgAssert(!IsFinalized(),("Finalizing Composite object %s twice",Script::FindChecksumName(GetID())));
|
|
CBaseComponent *p_component = mp_component_list;
|
|
while (p_component)
|
|
{
|
|
p_component->Finalize();
|
|
p_component = p_component->GetNext();
|
|
}
|
|
m_composite_object_flags.Set(CO_FINALIZED);
|
|
|
|
// now that the component is finalized,
|
|
// update the components that depend
|
|
// on the position of the object
|
|
Teleport();
|
|
}
|
|
|
|
|
|
// RefreshFromStructure is passed the same parameters as the above
|
|
// but will use them to update
|
|
void CCompositeObject::RefreshFromStructure( Script::CStruct* pParams )
|
|
{
|
|
// Make sure they are not trying to change the id to something else
|
|
#ifdef __NOPT_ASSERT__
|
|
uint32 NameChecksum;
|
|
if ( pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &NameChecksum ))
|
|
{
|
|
Dbg_MsgAssert(GetID() == NameChecksum,("Attempting to refresh id from %s to %s\n", Script::FindChecksumName(GetID()),NameChecksum));
|
|
}
|
|
#endif
|
|
|
|
// Update the position
|
|
// Note this is just cut and paste from above
|
|
// so if we're going to be doing more initting of the composite object
|
|
// from a script struct, then we might want to factor it out
|
|
SkateScript::GetPosition(pParams,&m_pos);
|
|
|
|
// Now iterate over the components, and Refresh them
|
|
CBaseComponent* pComponent = mp_component_list;
|
|
while ( pComponent )
|
|
{
|
|
pComponent->RefreshFromStructure(pParams);
|
|
pComponent = pComponent->mp_next;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CBaseComponent* CCompositeObject::GetComponent( uint32 id ) const
|
|
{
|
|
CBaseComponent *p_component = mp_component_list;
|
|
while (p_component)
|
|
{
|
|
if (p_component->GetType() == id)
|
|
{
|
|
return p_component;
|
|
}
|
|
p_component = p_component->GetNext();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CCompositeObject::CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
|
|
{
|
|
|
|
|
|
// This should probably go in some debug component?
|
|
switch ( Checksum )
|
|
{
|
|
// @script | Obj_PrintDetails | This is a debug function. Add this
|
|
// command to an object's script
|
|
// and it will print the details. Add anything you want to the Dbg_Message( )
|
|
// below.
|
|
// @uparmopt "string" | text to print
|
|
case 0xc2404947: // Obj_PrintDetails
|
|
{
|
|
const char* pText;
|
|
if ( pParams->GetText( NONAME, &pText ) )
|
|
{
|
|
Dbg_Message( "%s", pText );
|
|
}
|
|
Dbg_Message( "Obj details:\n pos %f %f %f\n time %d\n type %d",
|
|
m_pos[ X ], m_pos[ Y ], m_pos[ Z ], Tmr::GetTime(), m_type );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | CreateComponentFromStructure |
|
|
case 0x406998a5: // CreateComponentFromStructure
|
|
{
|
|
CreateComponentFromStructure( pParams, NULL );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_GetPosition |
|
|
case 0xe90aad2d: // Obj_GetPosition
|
|
{
|
|
pScript->GetParams()->AddVector( CRCD(0x7f261953,"pos"), m_pos[X], m_pos[Y], m_pos[Z] );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_SetPosition | Sets the world position of the object.
|
|
// @parmopt vector | Position | | Position to give to the object.
|
|
case 0xf7251a64: // Obj_SetPosition
|
|
{
|
|
pParams->GetVector(CRCD(0xb9d31b0a,"Position"),&m_pos);
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_SetOrientation |
|
|
// @parmopt float | x | 0.0 | The x angle, in degrees
|
|
// @parmopt float | y | 0.0 | The y angle, in degrees
|
|
// @parmopt float | z | 0.0 | The z angle, in degrees
|
|
// @parmopt vector | dir | (0,0,1) | Direction vector (an alternative to specifying angles)
|
|
// The direction vector does not need to be normalized.
|
|
case 0xc6f3baa5: // Obj_SetOrientation
|
|
{
|
|
Mth::Vector dir(0.0f,0.0f,1.0f);
|
|
if (pParams->GetVector(CRCD(0x455485ef,"dir"),&dir))
|
|
{
|
|
Mth::Matrix mat;
|
|
mat.Ident();
|
|
|
|
mat[Z]=dir;
|
|
mat[Z].Normalize();
|
|
mat[X] = Mth::CrossProduct(mat[Y], mat[Z]);
|
|
mat[X].Normalize();
|
|
mat[Y] = Mth::CrossProduct(mat[Z], mat[X]);
|
|
mat[Y].Normalize();
|
|
|
|
SetMatrix(mat);
|
|
SetDisplayMatrix(mat);
|
|
}
|
|
else
|
|
{
|
|
float p=0.0f;
|
|
float h=0.0f;
|
|
float r=0.0f;
|
|
pParams->GetFloat(CRCD(0x7323e97c,"x"),&p);
|
|
pParams->GetFloat(CRCD(0x424d9ea,"y"),&h);
|
|
pParams->GetFloat(CRCD(0x9d2d8850,"z"),&r);
|
|
p*=3.141592654f/180.0f;
|
|
h*=3.141592654f/180.0f;
|
|
r*=3.141592654f/180.0f;
|
|
Mth::Matrix mat(p,h,r);
|
|
SetMatrix(mat);
|
|
SetDisplayMatrix(mat);
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_ForceUpdate | Does a single call to the object's Update function
|
|
case 0xc1bff0f3: // Obj_ForceUpdate
|
|
{
|
|
Update();
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | Obj_GetVelocity |
|
|
case 0x11fe9f71: // Obj_GetVelocity
|
|
{
|
|
pScript->GetParams()->AddVector( CRCD(0x0c4c809e,"vel"), m_vel[X], m_vel[Y], m_vel[Z] );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | GetSpeed |
|
|
case 0xc0caac4a: // GetSpeed
|
|
{
|
|
pScript->GetParams()->AddFloat( CRCD(0xf0d90109,"speed"), m_vel.Length() );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | Hide | hides object
|
|
case 0x5b6634d4: // Hide
|
|
{
|
|
Hide( true );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | Unhide | unhides object
|
|
case 0xb60d1f35: // Unhide
|
|
{
|
|
Hide( false );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
// @script | IsHidden | checks the hide flag of object
|
|
case 0xb16619ae: // IsHidden
|
|
{
|
|
return m_composite_object_flags.Test(CO_HIDDEN);
|
|
}
|
|
break;
|
|
|
|
// @script | Move |
|
|
// @parmopt float | x | 0.0 | x component
|
|
// @parmopt float | y | 0.0 | y component
|
|
// @parmopt float | z | 0.0 | z component
|
|
case 0x10c1c887: // Move
|
|
{
|
|
float distance = 0.0f;
|
|
pParams->GetFloat(CRCD(0x7323e97c, "x"), &distance);
|
|
m_pos += distance * m_matrix[X];
|
|
distance = 0.0f;
|
|
pParams->GetFloat(CRCD(0x424d9ea, "y"), &distance);
|
|
m_pos += distance * m_matrix[Y];
|
|
distance = 0.0f;
|
|
pParams->GetFloat(CRCD(0x9d2d8850, "z"), &distance);
|
|
m_pos += distance * m_matrix[Z];
|
|
return true;
|
|
}
|
|
|
|
case 0xce0ca665: // Suspend
|
|
Suspend(true);
|
|
return true;
|
|
break;
|
|
|
|
case 0xca3c59a6: // Unsuspend
|
|
Suspend(false);
|
|
return true;
|
|
break;
|
|
|
|
case 0x28656d12: // Pause
|
|
Pause(true);
|
|
return true;
|
|
break;
|
|
|
|
case 0xff85d4d7: // Unpause
|
|
Pause(false);
|
|
return true;
|
|
break;
|
|
|
|
// MOVED FROM CMOVINGOBJECT.CPP
|
|
|
|
// @script | Obj_GetCollision | Checks to see if there is a collision along a line in front
|
|
// of the object.
|
|
// @parmopt float | Height | 3.0 | Height above origin of start point in feet
|
|
// @parmopt float | Lnegth | 3.0 | length of ocllision line in feet
|
|
// @flag side | Check side collision instead of forward collision
|
|
// @flag debug | display green debug lines at each collision test, white if there is a collision
|
|
case 0x168b09c: // Obj_GetCollision
|
|
{
|
|
CFeeler feeler;
|
|
//if (pParams->ContainsFlag(0xc4e78e22/*All*/))
|
|
|
|
float length = 3.0f;
|
|
pParams->GetFloat(0xfe82614d /*length*/,&length);
|
|
length *= 12.0f;
|
|
float height = 3.0f;
|
|
pParams->GetFloat(0xab21af0 /*height*/,&height);
|
|
height *= 12.0f;
|
|
|
|
feeler.m_start = m_pos + m_matrix[Y] * height;;
|
|
feeler.m_end = feeler.m_start;
|
|
if (pParams->ContainsFlag(0xdc7ee44a/*side*/))
|
|
{
|
|
feeler.m_end += length * m_matrix[X];
|
|
}
|
|
else
|
|
{
|
|
feeler.m_end += length * m_matrix[Z];
|
|
}
|
|
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
if (pParams->ContainsFlag(0x935ab858/*debug*/))
|
|
{
|
|
feeler.DebugLine(255,255,255); // White line = collision
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
if (pParams->ContainsFlag(0x935ab858/*debug*/))
|
|
{
|
|
feeler.DebugLine(0,255,0); // green line = no collision
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
// @script | Obj_GetOrientation | Gets the X,Y,Z vector representing orientation
|
|
case 0xb9ed09d2: // Obj_GetOrientation
|
|
{
|
|
Mth::Vector atVector;
|
|
atVector = m_display_matrix[Mth::AT];
|
|
atVector.Normalize();
|
|
|
|
pScript->GetParams()->AddFloat( "x", atVector[X] );
|
|
pScript->GetParams()->AddFloat( "y", atVector[Y] );
|
|
pScript->GetParams()->AddFloat( "z", atVector[Z] );
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case 0xd96c0a20: // Obj_GetDistToNode
|
|
{
|
|
uint32 node_name=0;
|
|
pParams->GetChecksum(NONAME,&node_name);
|
|
int node = SkateScript::FindNamedNode(node_name);
|
|
Mth::Vector node_pos;
|
|
SkateScript::GetPosition(node, &node_pos);
|
|
pScript->GetParams()->AddFloat("Dist",(m_pos-node_pos).Length());
|
|
return true;
|
|
}
|
|
|
|
// @script | Obj_GetDistanceToObject | Calculates the distance to the specified object,
|
|
// and puts the result, in units of feet, into ObjectDistance
|
|
// @uparmopt name | Name of the object. May be Skater.
|
|
case 0x4af57bbb: // Obj_GetDistanceToObject
|
|
{
|
|
uint32 object_name=0;
|
|
pParams->GetChecksum(NONAME,&object_name);
|
|
CCompositeObject* p_obj=(CCompositeObject*)Obj::ResolveToObject(object_name);
|
|
|
|
float dist=0.0f;
|
|
if (p_obj)
|
|
{
|
|
Mth::Vector d = m_pos - p_obj->GetPos();
|
|
dist=d.Length()/12.0f;
|
|
}
|
|
pScript->GetParams()->AddFloat("ObjectDistance",dist);
|
|
return true;
|
|
}
|
|
|
|
// @script | Obj_GetOrientationToObject |
|
|
// @uparm name | Name of the object. May be Skater.
|
|
case 0x2a26ffa2: // Obj_GetOrientationToObject
|
|
{
|
|
uint32 object_name=0;
|
|
pParams->GetChecksum(NONAME,&object_name,Script::ASSERT);
|
|
CCompositeObject* p_obj=(CCompositeObject*)Obj::ResolveToObject(object_name);
|
|
Dbg_MsgAssert( p_obj, ( "Couldn't find object %s to get orientation", Script::FindChecksumName(object_name) ) );
|
|
|
|
float dotProd=0.0f;
|
|
float orientation=0.0f;
|
|
|
|
if ( p_obj )
|
|
{
|
|
// first get the object's current right vector
|
|
Mth::Vector atVector;
|
|
atVector = m_matrix[Mth::RIGHT];
|
|
atVector[W] = 0.0f;
|
|
atVector.Normalize();
|
|
|
|
// now take the vector to the object
|
|
Mth::Vector toObjVector;
|
|
toObjVector = p_obj->m_pos - m_pos;
|
|
toObjVector[W] = 0.0f;
|
|
toObjVector.Normalize();
|
|
|
|
// dot product tells us whether the right
|
|
// vector is on the left or the right hand
|
|
// side of the toObject vector
|
|
dotProd = Mth::DotProduct( atVector, toObjVector );
|
|
|
|
// subtracting 90 gets us whether the at vector is on
|
|
// the left or the right...
|
|
orientation = Mth::RadToDeg( Mth::Kenacosf( dotProd ) ) - 90.0f; // add 90 to get the at vector
|
|
}
|
|
|
|
pScript->GetParams()->AddFloat(CRCD(0x7c6a7d7c,"DotProd"),dotProd);
|
|
pScript->GetParams()->AddFloat(CRCD(0xc97f3aa9,"Orientation"),orientation);
|
|
return true;
|
|
}
|
|
|
|
// @script | Backwards | Returns true if the skater is
|
|
// facing backwards from his direction of travel.
|
|
case CRCC(0xf8cfd515, "Backwards"):
|
|
return Mth::DotProduct(m_vel, m_matrix[Z]) < 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
|
|
|
|
// @script | SpeedEquals | true if skater speed is within 0.1
|
|
// of the specified speed
|
|
// @uparm 1.0 | speed
|
|
case CRCC(0x7dcc5fb9, "SpeedEquals"):
|
|
{
|
|
float TestSpeed = 0;
|
|
pParams->GetFloat(NO_NAME, &TestSpeed);
|
|
return Mth::Abs(m_vel.Length() - TestSpeed) < 0.1f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
|
|
}
|
|
|
|
// @script | SpeedGreaterThan | true if the skater speed is
|
|
// greater than the specified speed
|
|
// @uparm 1.0 | speed
|
|
case CRCC(0xe5df66d7, "SpeedGreaterThan"):
|
|
{
|
|
float TestSpeed = 0;
|
|
pParams->GetFloat(NO_NAME, &TestSpeed);
|
|
return m_vel.Length() > TestSpeed ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
|
|
}
|
|
|
|
// @script | SpeedLessThan | true if the skater speed is
|
|
// less than the specified speed
|
|
// @uparm 1.0 | speed
|
|
case CRCC(0xdd468509, "SpeedLessThan"):
|
|
{
|
|
float TestSpeed = 0;
|
|
pParams->GetFloat(NO_NAME, &TestSpeed);
|
|
return m_vel.Length() < TestSpeed ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
Dbg_MsgAssert(IsFinalized(),("CallMemberFunction %s to UnFinalized Composite object %s",Script::FindChecksumName(Checksum),Script::FindChecksumName(GetID())));
|
|
CBaseComponent *p_component = mp_component_list;
|
|
while (p_component)
|
|
{
|
|
switch (p_component->CallMemberFunction(Checksum, pParams, pScript))
|
|
{
|
|
case CBaseComponent::MF_TRUE:
|
|
return true;
|
|
case CBaseComponent::MF_FALSE:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
p_component = p_component->GetNext();
|
|
}
|
|
|
|
return CObject::CallMemberFunction( Checksum, pParams, pScript );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Used by the script debugger code (gel\scripting\debugger.cpp) to fill in a structure
|
|
// for transmitting to the monitor.exe utility running on the PC.
|
|
|
|
// This adds basic info about the CCompositeObject such as the id, position etc.
|
|
// It also adds the debug info for each of the components in the list.
|
|
// If classes derived from CCompositeObject override this function, then they should call
|
|
// CCompositeObject::GetDebugInfo at the end.
|
|
// It would not matter too much if they called it at the start instead, that would just mean
|
|
// that derived classes info would appear at the end of the structure (after the list of components)
|
|
// rather than at the start.
|
|
void CCompositeObject::GetDebugInfo(Script::CStruct *p_info)
|
|
{
|
|
#ifdef __DEBUG_CODE__
|
|
Dbg_MsgAssert(p_info,("NULL p_info sent to CCompositeObject::GetDebugInfo"));
|
|
|
|
int node_index=SkateScript::FindNamedNode(m_id,false); // false means don't assert if not found.
|
|
if (node_index >= 0 && m_id) // The && m_id is cos FindNamedNode erroneously returns 0 in that case.
|
|
{
|
|
Script::CStruct *p_node=SkateScript::GetNode(node_index);
|
|
p_info->AddInteger("NodeIndex",node_index);
|
|
p_info->AddStructure("NodeInfo",p_node);
|
|
}
|
|
else
|
|
{
|
|
p_info->AddChecksum(NONAME,CRCD(0xc39ae7b3,"NoNode"));
|
|
}
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
p_info->AddInteger("CPUTime",m_update_time);
|
|
p_info->AddInteger("m_total_script_update_time",m_total_script_update_time);
|
|
p_info->AddInteger("m_do_game_logic_time",m_do_game_logic_time);
|
|
#endif
|
|
|
|
CObject::GetDebugInfo(p_info);
|
|
|
|
uint32 type_name=0;
|
|
|
|
switch (m_type)
|
|
{
|
|
case SKATE_TYPE_UNDEFINED:
|
|
type_name=CRCD(0x4cb79b3b,"SKATE_TYPE_UNDEFINED");
|
|
break;
|
|
case SKATE_TYPE_SKATER:
|
|
type_name=CRCD(0xdbb4aeb6,"SKATE_TYPE_SKATER");
|
|
break;
|
|
case SKATE_TYPE_PED:
|
|
type_name=CRCD(0xa88987b7,"SKATE_TYPE_PED");
|
|
break;
|
|
case SKATE_TYPE_CAR:
|
|
type_name=CRCD(0x2651eacb,"SKATE_TYPE_CAR");
|
|
break;
|
|
case SKATE_TYPE_GAME_OBJ:
|
|
type_name=CRCD(0x84f6eb36,"SKATE_TYPE_GAME_OBJ");
|
|
break;
|
|
case SKATE_TYPE_BOUNCY_OBJ:
|
|
type_name=CRCD(0x5f4886c1,"SKATE_TYPE_BOUNCY_OBJ");
|
|
break;
|
|
case SKATE_TYPE_CASSETTE:
|
|
type_name=CRCD(0xd5021817,"SKATE_TYPE_CASSETTE");
|
|
break;
|
|
case SKATE_TYPE_ANIMATING_OBJECT:
|
|
type_name=CRCD(0x42018493,"SKATE_TYPE_ANIMATING_OBJECT");
|
|
break;
|
|
case SKATE_TYPE_CROWN:
|
|
type_name=CRCD(0xf4f7488e,"SKATE_TYPE_CROWN");
|
|
break;
|
|
case SKATE_TYPE_PARTICLE:
|
|
type_name=CRCD(0x8feeb37e,"SKATE_TYPE_PARTICLE");
|
|
break;
|
|
case SKATE_TYPE_REPLAY_DUMMY:
|
|
type_name=CRCD(0xf4ac5a55,"SKATE_TYPE_REPLAY_DUMMY");
|
|
break;
|
|
case SKATE_TYPE_COMPOSITE:
|
|
type_name=CRCD(0x8a61a62c,"SKATE_TYPE_COMPOSITE");
|
|
break;
|
|
default:
|
|
type_name=0;
|
|
break;
|
|
|
|
}
|
|
if (type_name)
|
|
{
|
|
p_info->AddChecksum("m_type",type_name);
|
|
}
|
|
else
|
|
{
|
|
// If the value is missing from the above switch statement then just add it as an integer.
|
|
p_info->AddInteger("m_type",m_type);
|
|
}
|
|
|
|
p_info->AddChecksum("m_object_flags",m_object_flags);
|
|
|
|
// CCompositeObject stuff
|
|
if (m_composite_object_flags.Test(CO_PAUSED))
|
|
{
|
|
p_info->AddChecksum(NONAME,Script::GenerateCRC("Paused"));
|
|
}
|
|
|
|
p_info->AddVector("m_pos",m_pos.GetX(),m_pos.GetY(),m_pos.GetZ());
|
|
p_info->AddVector("m_vel",m_vel.GetX(),m_vel.GetY(),m_vel.GetZ());
|
|
|
|
Script::CArray *p_mat=new Script::CArray;
|
|
p_mat->SetSizeAndType(3,ESYMBOLTYPE_VECTOR);
|
|
Script::CVector *p_vec=new Script::CVector;
|
|
p_vec->mX=m_matrix[X][X]; p_vec->mY=m_matrix[X][Y]; p_vec->mZ=m_matrix[X][Z];
|
|
p_mat->SetVector(0,p_vec);
|
|
p_vec=new Script::CVector;
|
|
p_vec->mX=m_matrix[Y][X]; p_vec->mY=m_matrix[Y][Y]; p_vec->mZ=m_matrix[Y][Z];
|
|
p_mat->SetVector(1,p_vec);
|
|
p_vec=new Script::CVector;
|
|
p_vec->mX=m_matrix[Z][X]; p_vec->mY=m_matrix[Z][Y]; p_vec->mZ=m_matrix[Z][Z];
|
|
p_mat->SetVector(2,p_vec);
|
|
p_info->AddArrayPointer("m_matrix",p_mat);
|
|
|
|
p_mat=new Script::CArray;
|
|
p_mat->SetSizeAndType(3,ESYMBOLTYPE_VECTOR);
|
|
p_vec=new Script::CVector;
|
|
p_vec->mX=m_display_matrix[X][X]; p_vec->mY=m_display_matrix[X][Y]; p_vec->mZ=m_display_matrix[X][Z];
|
|
p_mat->SetVector(0,p_vec);
|
|
p_vec=new Script::CVector;
|
|
p_vec->mX=m_display_matrix[Y][X]; p_vec->mY=m_display_matrix[Y][Y]; p_vec->mZ=m_display_matrix[Y][Z];
|
|
p_mat->SetVector(1,p_vec);
|
|
p_vec=new Script::CVector;
|
|
p_vec->mX=m_display_matrix[Z][X]; p_vec->mY=m_display_matrix[Z][Y]; p_vec->mZ=m_display_matrix[Z][Z];
|
|
p_mat->SetVector(2,p_vec);
|
|
p_info->AddArrayPointer("m_display_matrix",p_mat);
|
|
|
|
Script::CStruct *p_components_structure=new Script::CStruct;
|
|
CBaseComponent *p_comp=mp_component_list;
|
|
while (p_comp)
|
|
{
|
|
Script::CStruct *p_component_structure=new Script::CStruct;
|
|
p_comp->GetDebugInfo(p_component_structure);
|
|
|
|
// Using the component type as the name given to the structure, since the
|
|
// type is the checksum of the type name.
|
|
p_components_structure->AddStructurePointer(p_comp->GetType(),p_component_structure);
|
|
p_comp=p_comp->GetNext();
|
|
}
|
|
|
|
p_info->AddStructurePointer("Components",p_components_structure);
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CCompositeObject::SetTeleported( bool update_components )
|
|
{
|
|
m_composite_object_flags.Set(CO_TELEPORTED);
|
|
|
|
if ( update_components )
|
|
{
|
|
Teleport();
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
}
|
|
|