0ad/source/simulation/Entity.cpp
2004-07-12 16:08:41 +00:00

359 lines
9.0 KiB
C++
Executable File

// Last modified: May 15 2004, Mark Thompson (mark@wildfiregames.com)
#include "precompiled.h"
#include "Entity.h"
#include "EntityManager.h"
#include "BaseEntityCollection.h"
#include "Renderer.h"
#include "Model.h"
#include "Terrain.h"
#include "Collision.h"
#include "PathfindEngine.h"
CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
{
m_position = position;
m_orientation = orientation;
m_ahead.x = sin( m_orientation );
m_ahead.y = cos( m_orientation );
m_base.associate( this, "template", ( void( IPropertyOwner::* )() )&CEntity::loadBase );
m_name.associate( this, "name" );
m_speed.associate( this, "speed" );
m_turningRadius.associate( this, "turningRadius" );
m_position.associate( this, "position", ( void( IPropertyOwner::* )() )&CEntity::teleport );
m_orientation.associate( this, "orientation", ( void( IPropertyOwner::* )() )&CEntity::reorient );
// Set our parent unit and build us an actor.
m_actor = NULL;
m_bounds = NULL;
m_base = base;
loadBase();
snapToGround();
updateActorTransforms();
}
CEntity::~CEntity()
{
for( size_t i = 0; i < m_base->m_inheritors.size(); i++ )
if( m_base->m_inheritors[i] == this )
m_base->m_inheritors.erase( m_base->m_inheritors.begin() + i );
if( m_actor )
{
g_UnitMan.RemoveUnit( m_actor );
delete( m_actor );
}
if( m_bounds ) delete( m_bounds );
}
void CEntity::loadBase()
{
if( m_actor )
{
g_UnitMan.RemoveUnit( m_actor );
delete( m_actor );
}
if( m_bounds )
{
delete( m_bounds );
}
m_actor = new CUnit(m_base->m_actorObject,m_base->m_actorObject->m_Model->Clone());
// Register the actor with the renderer.
g_UnitMan.AddUnit( m_actor );
// Set up our instance data
m_base->m_inheritors.push_back( this );
rebuild();
if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
{
m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle );
}
else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB )
{
m_bounds = new CBoundingBox( m_position.X, m_position.Z, m_ahead, m_base->m_bound_box );
}
}
bool isWaypoint( CEntity* e )
{
return( e->m_base->m_name == CStr( "Waypoint" ) );
}
void CEntity::updateActorTransforms()
{
CMatrix3D m;
m._11 = -m_ahead.y; m._12 = 0.0f; m._13 = -m_ahead.x; m._14 = m_position.X;
m._21 = 0.0f; m._22 = 1.0f; m._23 = 0.0f; m._24 = m_position.Y;
m._31 = m_ahead.x; m._32 = 0.0f; m._33 = -m_ahead.y; m._34 = m_position.Z;
m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f;
/* Equivalent to:
m.SetYRotation( m_orientation );
m.Translate( m_position );
But the matrix multiplication seemed such a waste when we already have a forward vector
*/
m_actor->GetModel()->SetTransform( m );
}
float CEntity::getExactGroundLevel( float x, float y )
{
// TODO MT: If OK with Rich, move to terrain core. Once this works, that is.
x /= 4.0f;
y /= 4.0f;
int xi = (int)floor( x );
int yi = (int)floor( y );
float xf = x - (float)xi;
float yf = y - (float)yi;
u16* heightmap = g_Terrain.GetHeightMap();
unsigned long mapsize = g_Terrain.GetVerticesPerSide();
assert( ( xi >= 0 ) && ( xi < (int)mapsize ) && ( yi >= 0 ) && ( yi < (int)mapsize ) );
// In non-debug builds, do something nicer than crashing
if (! ( ( xi >= 0 ) && ( xi < (int)mapsize ) && ( yi >= 0 ) && ( yi < (int)mapsize ) ) )
{
return 0.0f;
}
float h00 = heightmap[yi*mapsize + xi];
float h01 = heightmap[yi*mapsize + xi + mapsize];
float h10 = heightmap[yi*mapsize + xi + 1];
float h11 = heightmap[yi*mapsize + xi + mapsize + 1];
/*
if( xf < ( 1.0f - yf ) )
{
return( HEIGHT_SCALE * ( ( 1 - xf - yf ) * h00 + xf * h10 + yf * h01 ) );
}
else
return( HEIGHT_SCALE * ( ( xf + yf - 1 ) * h11 + ( 1 - xf ) * h01 + ( 1 - yf ) * h10 ) );
*/
/*
if( xf > yf )
{
return( HEIGHT_SCALE * ( ( 1 - xf ) * h00 + ( xf - yf ) * h10 + yf * h11 ) );
}
else
return( HEIGHT_SCALE * ( ( 1 - yf ) * h00 + ( yf - xf ) * h01 + xf * h11 ) );
*/
return( HEIGHT_SCALE * ( ( 1 - yf ) * ( ( 1 - xf ) * h00 + xf * h10 ) + yf * ( ( 1 - xf ) * h01 + xf * h11 ) ) );
}
void CEntity::snapToGround()
{
m_position.Y = getExactGroundLevel( m_position.X, m_position.Z );
}
void CEntity::update( float timestep )
{
while( !m_orderQueue.empty() )
{
CEntityOrder* current = &m_orderQueue.front();
switch( current->m_type )
{
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_COLLISION:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
if( processGotoNoPathing( current, timestep ) ) break;
return;
case CEntityOrder::ORDER_GOTO:
if( processGoto( current, timestep ) ) break;
return;
case CEntityOrder::ORDER_PATROL:
if( processPatrol( current, timestep ) ) break;
return;
default:
assert( 0 && "Invalid entity order" );
}
}
if( m_actor->GetModel()->GetAnimation() != m_actor->GetObject()->m_IdleAnim )
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_IdleAnim );
}
void CEntity::dispatch( CMessage* msg )
{
switch( msg->type )
{
case CMessage::EMSG_TICK:
break;
case CMessage::EMSG_INIT:
if( m_base->m_name == CStr( "Prometheus Dude" ) )
{
if( getCollisionObject( this ) )
{
// Prometheus telefragging. (Appeared inside another object)
g_EntityManager.kill( me );
return;
}
std::vector<HEntity>* waypoints = g_EntityManager.matches( isWaypoint );
while( !waypoints->empty() )
{
CEntityOrder patrol;
size_t id = rand() % waypoints->size();
std::vector<HEntity>::iterator it = waypoints->begin();
it += id;
HEntity waypoint = *it;
patrol.m_type = CEntityOrder::ORDER_PATROL;
patrol.m_data[0].location.x = waypoint->m_position.X;
patrol.m_data[0].location.y = waypoint->m_position.Z;
pushOrder( patrol );
waypoints->erase( it );
}
delete( waypoints );
}
break;
}
}
void CEntity::pushOrder( CEntityOrder& order )
{
m_orderQueue.push_back( order );
}
void CEntity::repath()
{
CVector2D destination;
if( m_orderQueue.empty() ) return;
while( !m_orderQueue.empty() &&
( ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_COLLISION )
|| ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_NOPATHING )
|| ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_SMOOTHED ) ) )
{
destination = m_orderQueue.front().m_data[0].location;
m_orderQueue.pop_front();
}
g_Pathfinder.requestPath( me, destination );
}
void CEntity::reorient()
{
m_ahead.x = sin( m_orientation );
m_ahead.y = cos( m_orientation );
if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*)m_bounds)->setOrientation( m_ahead );
updateActorTransforms();
}
void CEntity::teleport()
{
snapToGround();
updateActorTransforms();
m_bounds->setPosition( m_position.X, m_position.Z );
repath();
}
void CEntity::render()
{
// Rich! Help! ;)
// HACK: As in this entire function is a...
if( !m_orderQueue.empty() )
{
std::deque<CEntityOrder>::iterator it;
CBoundingObject* destinationCollisionObject;
float x0, y0, x, y;
x = m_orderQueue.front().m_data[0].location.x;
y = m_orderQueue.front().m_data[0].location.y;
for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ )
{
if( it->m_type == CEntityOrder::ORDER_PATROL )
break;
x = it->m_data[0].location.x;
y = it->m_data[0].location.y;
}
destinationCollisionObject = getContainingObject( CVector2D( x, y ) );
glShadeModel( GL_FLAT );
glBegin( GL_LINE_STRIP );
glVertex3f( m_position.X, m_position.Y + 0.25f, m_position.Z );
x = m_position.X;
y = m_position.Z;
for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ )
{
x0 = x; y0 = y;
x = it->m_data[0].location.x;
y = it->m_data[0].location.y;
rayIntersectionResults r;
CVector2D fwd( x - x0, y - y0 );
float l = fwd.length();
fwd = fwd.normalize();
CVector2D rgt = fwd.beta();
if( getRayIntersection( CVector2D( x0, y0 ), fwd, rgt, l, m_bounds->m_radius, destinationCollisionObject, &r ) )
{
glEnd();
glBegin( GL_LINES );
glColor3f( 1.0f, 0.0f, 0.0f );
glVertex3f( x0 + fwd.x * r.distance, getExactGroundLevel( x0 + fwd.x * r.distance, y0 + fwd.y * r.distance ) + 0.25f, y0 + fwd.y * r.distance );
glVertex3f( r.position.x, getExactGroundLevel( r.position.x, r.position.y ) + 0.25f, r.position.y );
glEnd();
glBegin( GL_LINE_STRIP );
glVertex3f( x0, getExactGroundLevel( x0, y0 ), y0 );
}
switch( it->m_type )
{
case CEntityOrder::ORDER_GOTO:
glColor3f( 1.0f, 0.0f, 0.0f ); break;
case CEntityOrder::ORDER_GOTO_COLLISION:
glColor3f( 1.0f, 0.5f, 0.5f ); break;
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
glColor3f( 0.5f, 0.5f, 0.5f ); break;
case CEntityOrder::ORDER_PATROL:
glColor3f( 0.0f, 1.0f, 0.0f ); break;
default:
continue;
}
glVertex3f( x, getExactGroundLevel( x, y ) + 0.25f, y );
}
glEnd();
glShadeModel( GL_SMOOTH );
}
glColor3f( 1.0f, 1.0f, 1.0f );
if( getCollisionObject( this ) ) glColor3f( 0.5f, 0.5f, 1.0f );
m_bounds->render( getExactGroundLevel( m_position.X, m_position.Z ) + 0.25f ); //m_position.Y + 0.25f );
}
void PASAPScenario()
{
// Got rid of all the hardcoding that was here.
}