1
0
forked from 0ad/0ad

Changed hotkey handling for groups and bookmarks; small pathfinding fixes.

This was SVN commit r809.
This commit is contained in:
MarkT 2004-07-23 10:56:52 +00:00
parent 1a33a9afc3
commit befae8627d
24 changed files with 391 additions and 175 deletions

View File

@ -156,11 +156,11 @@ bool CHFTracer::RayIntersect(CVector3D& origin,CVector3D& dir,int& x,int& z,CVec
// catch travelling off the map
if( ( cx < 0 ) && ( sx < 0 ) )
return( false );
if( ( cx >= m_MapSize ) && ( sx > 0 ) )
if( ( cx >= (int)m_MapSize ) && ( sx > 0 ) )
return( false );
if( ( cz < 0 ) && ( sz < 0 ) )
return( false );
if( ( cz >= m_MapSize ) && ( sz > 0 ) )
if( ( cz >= (int)m_MapSize ) && ( sz > 0 ) )
return( false );
}

View File

@ -150,6 +150,27 @@ CMiniPatch* CTerrain::GetTile(int32_t x,int32_t z)
return &patch->m_MiniPatches[z%PATCH_SIZE][x%PATCH_SIZE];
}
float CTerrain::getExactGroundLevel( float x, float y ) const
{
x /= (float)CELL_SIZE;
y /= (float)CELL_SIZE;
int xi = (int)floor( x );
int yi = (int)floor( y );
float xf = x - (float)xi;
float yf = y - (float)yi;
assert( isOnMap( x, y ) );
if( !isOnMap( x, y ) )
return 0.0f;
float h00 = m_Heightmap[yi*m_MapSize + xi];
float h01 = m_Heightmap[yi*m_MapSize + xi + m_MapSize];
float h10 = m_Heightmap[yi*m_MapSize + xi + 1];
float h11 = m_Heightmap[yi*m_MapSize + xi + m_MapSize + 1];
return( HEIGHT_SCALE * ( ( 1 - yf ) * ( ( 1 - xf ) * h00 + xf * h10 ) + yf * ( ( 1 - xf ) * h01 + xf * h11 ) ) );
}
///////////////////////////////////////////////////////////////////////////////
// Resize: resize this terrain to the given size (in patches per side)

View File

@ -12,6 +12,7 @@
#include "Patch.h"
#include "Vector3D.h"
#include "Vector2D.h"
///////////////////////////////////////////////////////////////////////////////
// CTerrain: main terrain class; contains the heightmap describing elevation
@ -29,6 +30,17 @@ public:
// return number of patches along edge of the terrain
u32 GetPatchesPerSide() { return m_MapSizePatches; }
inline bool isOnMap( float x, float y ) const
{
return( ( x >= 0.0f ) && ( x <= (float)m_MapSize ) && ( y >= 0.0f ) && ( y <= (float)m_MapSize ) );
}
inline bool isOnMap( const CVector2D& v ) const
{
return( ( v.x >= 0.0f ) && ( v.x <= (float)m_MapSize ) && ( v.y >= 0.0f ) && ( v.y <= (float)m_MapSize ) );
}
float getExactGroundLevel( float x, float y ) const ;
inline float getExactGroundLevel( const CVector2D& v ) const { return( getExactGroundLevel( v.x, v.y ) ); }
// resize this terrain such that each side has given number of patches
void Resize(u32 size);

View File

@ -57,12 +57,41 @@ static SHotkeyInfo hotkeyInfo[] =
{ HOTKEY_CAMERA_PAN_RIGHT, "camera.pan.right", SDLK_RIGHT, 0 },
{ HOTKEY_CAMERA_PAN_FORWARD, "camera.pan.forward", SDLK_UP, 0 },
{ HOTKEY_CAMERA_PAN_BACKWARD, "camera.pan.backward", SDLK_DOWN, 0 },
{ HOTKEY_CAMERA_BOOKMARK_MODIFIER, "camera.bookmark.modifier", 0, 0 },
{ HOTKEY_CAMERA_BOOKMARK_0, "camera.bookmark.0", SDLK_F5, 0, },
{ HOTKEY_CAMERA_BOOKMARK_1, "camera.bookmark.1", SDLK_F6, 0, },
{ HOTKEY_CAMERA_BOOKMARK_2, "camera.bookmark.2", SDLK_F7, 0, },
{ HOTKEY_CAMERA_BOOKMARK_3, "camera.bookmark.3", SDLK_F8, 0, },
{ HOTKEY_CAMERA_BOOKMARK_4, "camera.bookmark.4", 0, 0, },
{ HOTKEY_CAMERA_BOOKMARK_5, "camera.bookmark.5", 0, 0, },
{ HOTKEY_CAMERA_BOOKMARK_6, "camera.bookmark.6", 0, 0, },
{ HOTKEY_CAMERA_BOOKMARK_7, "camera.bookmark.7", 0, 0, },
{ HOTKEY_CAMERA_BOOKMARK_8, "camera.bookmark.8", 0, 0, },
{ HOTKEY_CAMERA_BOOKMARK_9, "camera.bookmark.9", 0, 0, },
{ HOTKEY_CAMERA_BOOKMARK_SAVE, "camera.bookmark.save", 0, 0 },
{ HOTKEY_CAMERA_BOOKMARK_SNAP, "camera.bookmark.snap", 0, 0 },
{ HOTKEY_CONSOLE_TOGGLE, "console.toggle", SDLK_F1, 0 },
{ HOTKEY_SELECTION_ADD, "selection.add", SDLK_LSHIFT, SDLK_RSHIFT },
{ HOTKEY_SELECTION_REMOVE, "selection.remove", SDLK_LCTRL, SDLK_RCTRL },
{ HOTKEY_SELECTION_GROUP_0, "selection.group.0", SDLK_0, 0, },
{ HOTKEY_SELECTION_GROUP_1, "selection.group.1", SDLK_1, 0, },
{ HOTKEY_SELECTION_GROUP_2, "selection.group.2", SDLK_2, 0, },
{ HOTKEY_SELECTION_GROUP_3, "selection.group.3", SDLK_3, 0, },
{ HOTKEY_SELECTION_GROUP_4, "selection.group.4", SDLK_4, 0, },
{ HOTKEY_SELECTION_GROUP_5, "selection.group.5", SDLK_5, 0, },
{ HOTKEY_SELECTION_GROUP_6, "selection.group.6", SDLK_6, 0, },
{ HOTKEY_SELECTION_GROUP_7, "selection.group.7", SDLK_7, 0, },
{ HOTKEY_SELECTION_GROUP_8, "selection.group.8", SDLK_8, 0, },
{ HOTKEY_SELECTION_GROUP_9, "selection.group.9", SDLK_9, 0, },
{ HOTKEY_SELECTION_GROUP_10, "selection.group.10", 0, 0, },
{ HOTKEY_SELECTION_GROUP_11, "selection.group.11", 0, 0, },
{ HOTKEY_SELECTION_GROUP_12, "selection.group.12", 0, 0, },
{ HOTKEY_SELECTION_GROUP_13, "selection.group.13", 0, 0, },
{ HOTKEY_SELECTION_GROUP_14, "selection.group.14", 0, 0, },
{ HOTKEY_SELECTION_GROUP_15, "selection.group.15", 0, 0, },
{ HOTKEY_SELECTION_GROUP_16, "selection.group.16", 0, 0, },
{ HOTKEY_SELECTION_GROUP_17, "selection.group.17", 0, 0, },
{ HOTKEY_SELECTION_GROUP_18, "selection.group.18", 0, 0, },
{ HOTKEY_SELECTION_GROUP_19, "selection.group.19", 0, 0, },
{ HOTKEY_SELECTION_GROUP_ADD, "selection.group.add", SDLK_LSHIFT, SDLK_RSHIFT },
{ HOTKEY_SELECTION_GROUP_SAVE, "selection.group.save", SDLK_LCTRL, SDLK_RCTRL },
{ HOTKEY_SELECTION_GROUP_SNAP, "selection.group.snap", SDLK_LALT, SDLK_RALT },
@ -228,9 +257,22 @@ int hotkeyInputHandler( const SDL_Event* ev )
SDL_Event hotkeyNotification;
// Here's an interesting bit:
// If you have an event bound to, say, 'F', and another to, say, 'Ctrl+F', pressing
// 'F' while control is down will fire off both.
// To avoid this, set the modifier keys for /all/ events this key would trigger
// (Ctrl, for example, is both group-save and bookmark-save)
// but only send a HotkeyDown event for the event whos bindings most precisely
// match the conditions (i.e. the event with the highest number of auxiliary
// keys, providing they're all down)
if( ( ev->type == SDL_KEYDOWN ) || ( ev->type == SDL_MOUSEBUTTONDOWN ) )
{
// SDL-events bit
int closestMap = -1, closestMapMatch;
for( it = hotkeyMap[keycode].begin(); it < hotkeyMap[keycode].end(); it++ )
{
// Check to see if all auxiliary keys are down
@ -253,11 +295,20 @@ int hotkeyInputHandler( const SDL_Event* ev )
if( accept )
{
hotkeys[it->mapsTo] = true;
hotkeyNotification.type = SDL_HOTKEYDOWN;
hotkeyNotification.user.code = it->mapsTo;
SDL_PushEvent( &hotkeyNotification );
if( ( closestMap == -1 ) || ( it->requires.size() > closestMapMatch ) )
{
closestMap = it->mapsTo;
closestMapMatch = it->requires.size();
}
}
}
if( closestMap != -1 )
{
hotkeyNotification.type = SDL_HOTKEYDOWN;
hotkeyNotification.user.code = closestMap;
SDL_PushEvent( &hotkeyNotification );
}
// GUI bit... could do with some optimization later.
for( itGUI = hotkeyMapGUI[keycode].begin(); itGUI != hotkeyMapGUI[keycode].end(); itGUI++ )
{

View File

@ -42,12 +42,41 @@ enum
HOTKEY_CAMERA_PAN_RIGHT,
HOTKEY_CAMERA_PAN_FORWARD,
HOTKEY_CAMERA_PAN_BACKWARD,
HOTKEY_CAMERA_BOOKMARK_MODIFIER,
HOTKEY_CAMERA_BOOKMARK_0,
HOTKEY_CAMERA_BOOKMARK_1,
HOTKEY_CAMERA_BOOKMARK_2,
HOTKEY_CAMERA_BOOKMARK_3,
HOTKEY_CAMERA_BOOKMARK_4,
HOTKEY_CAMERA_BOOKMARK_5,
HOTKEY_CAMERA_BOOKMARK_6,
HOTKEY_CAMERA_BOOKMARK_7,
HOTKEY_CAMERA_BOOKMARK_8,
HOTKEY_CAMERA_BOOKMARK_9,
HOTKEY_CAMERA_BOOKMARK_SAVE,
HOTKEY_CAMERA_BOOKMARK_SNAP,
HOTKEY_CONSOLE_TOGGLE,
HOTKEY_SELECTION_ADD,
HOTKEY_SELECTION_REMOVE,
HOTKEY_SELECTION_GROUP_0,
HOTKEY_SELECTION_GROUP_1,
HOTKEY_SELECTION_GROUP_2,
HOTKEY_SELECTION_GROUP_3,
HOTKEY_SELECTION_GROUP_4,
HOTKEY_SELECTION_GROUP_5,
HOTKEY_SELECTION_GROUP_6,
HOTKEY_SELECTION_GROUP_7,
HOTKEY_SELECTION_GROUP_8,
HOTKEY_SELECTION_GROUP_9,
HOTKEY_SELECTION_GROUP_10,
HOTKEY_SELECTION_GROUP_11,
HOTKEY_SELECTION_GROUP_12,
HOTKEY_SELECTION_GROUP_13,
HOTKEY_SELECTION_GROUP_14,
HOTKEY_SELECTION_GROUP_15,
HOTKEY_SELECTION_GROUP_16,
HOTKEY_SELECTION_GROUP_17,
HOTKEY_SELECTION_GROUP_18,
HOTKEY_SELECTION_GROUP_19,
HOTKEY_SELECTION_GROUP_ADD,
HOTKEY_SELECTION_GROUP_SAVE,
HOTKEY_SELECTION_GROUP_SNAP,

View File

@ -72,10 +72,8 @@ void CSelectedEntities::renderOverlays()
glLoadIdentity();
float x, y;
CVector3D labelpos = (*it)->m_graphics_position - g_Camera.m_Orientation.GetLeft() * (*it)->m_bounds->m_radius;
labelpos.X += (*it)->m_bounds->m_offset.x;
labelpos.Z += (*it)->m_bounds->m_offset.y;
#ifdef SELECTION_TERRAIN_CONFORMANCE
labelpos.Y = (*it)->getExactGroundLevel( labelpos.X, labelpos.Z );
labelpos.Y = g_Terrain.getExactGroundLevel( labelpos.X, labelpos.Z );
#endif
g_Camera.GetScreenCoordinates( labelpos, x, y );
glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
@ -98,10 +96,8 @@ void CSelectedEntities::renderOverlays()
glLoadIdentity();
float x, y;
CVector3D labelpos = (*it)->m_graphics_position - g_Camera.m_Orientation.GetLeft() * (*it)->m_bounds->m_radius;
labelpos.X += (*it)->m_bounds->m_offset.x;
labelpos.Z += (*it)->m_bounds->m_offset.y;
#ifdef SELECTION_TERRAIN_CONFORMANCE
labelpos.Y = (*it)->getExactGroundLevel( labelpos.X, labelpos.Z );
labelpos.Y = g_Terrain.getExactGroundLevel( labelpos.X, labelpos.Z );
#endif
g_Camera.GetScreenCoordinates( labelpos, x, y );
glColor4f( 1.0f, 1.0f, 1.0f, 0.5f );
@ -178,11 +174,7 @@ CVector3D CSelectedEntities::getSelectionPosition()
CVector3D avg;
std::vector<CEntity*>::iterator it;
for( it = m_selected.begin(); it < m_selected.end(); it++ )
{
avg += (*it)->m_graphics_position;
avg.X += (*it)->m_bounds->m_offset.x;
avg.Z += (*it)->m_bounds->m_offset.y;
}
return( avg * ( 1.0f / m_selected.size() ) );
}
@ -303,11 +295,7 @@ CVector3D CSelectedEntities::getGroupPosition( u8 groupid )
CVector3D avg;
std::vector<CEntity*>::iterator it;
for( it = m_groups[groupid].begin(); it < m_groups[groupid].end(); it++ )
{
avg += (*it)->m_graphics_position;
avg.X += (*it)->m_bounds->m_offset.x;
avg.Z += (*it)->m_bounds->m_offset.y;
}
return( avg * ( 1.0f / m_groups[groupid].size() ) );
}
@ -382,7 +370,7 @@ bool CSelectedEntities::isContextValid( int contextOrder )
void CSelectedEntities::contextOrder( bool pushQueue )
{
std::vector<CEntity*>::iterator it;
CEntityOrder context;
CEntityOrder context, contextRandomized;
(int&)context.m_type = m_contextOrder;
CVector3D origin, dir;
g_Camera.BuildCameraRay( origin, dir );
@ -404,9 +392,32 @@ void CSelectedEntities::contextOrder( bool pushQueue )
break;
}
// Location randomizer, for group orders...
// Having the group turn up at the destination with /some/ sort of cohesion is good
// but tasking them all to the exact same point will leave them brawling for it
// at the other end (it shouldn't, but the PASAP pathfinder is too simplistic)
// Task them all to a point within a radius of the target, radius depends upon
// the number of units in the group.
float radius = 2.0f * sqrt( (float)m_selected.size() ); // A decent enough approximation
float _t, _x, _y;
for( it = m_selected.begin(); it < m_selected.end(); it++ )
if( (*it)->acceptsOrder( m_contextOrder, g_Mouseover.m_target ) )
g_Scheduler.pushFrame( ORDER_DELAY, (*it)->me, new CMessageOrder( context, pushQueue ) );
{
contextRandomized = context;
do
{
_x = (float)( rand() % 20000 ) / 10000.0f - 1.0f;
_y = (float)( rand() % 20000 ) / 10000.0f - 1.0f;
}
while( ( _x * _x ) + ( _y * _y ) > 1.0f );
contextRandomized.m_data[0].location.x += _x * radius;
contextRandomized.m_data[0].location.y += _y * radius;
g_Scheduler.pushFrame( ORDER_DELAY, (*it)->me, new CMessageOrder( contextRandomized, pushQueue ) );
}
}
@ -459,8 +470,6 @@ void CMouseoverEntities::update( float timestep )
for( it = onscreen->begin(); it < onscreen->end(); it++ )
{
CVector3D worldspace = (*it)->m_graphics_position;
worldspace.X += (*it)->m_bounds->m_offset.x;
worldspace.Z += (*it)->m_bounds->m_offset.y;
float x, y;
@ -634,10 +643,8 @@ void CMouseoverEntities::renderOverlays()
glLoadIdentity();
float x, y;
CVector3D labelpos = it->entity->m_graphics_position - g_Camera.m_Orientation.GetLeft() * it->entity->m_bounds->m_radius;
labelpos.X += it->entity->m_bounds->m_offset.x;
labelpos.Z += it->entity->m_bounds->m_offset.y;
#ifdef SELECTION_TERRAIN_CONFORMANCE
labelpos.Y = it->entity->getExactGroundLevel( labelpos.X, labelpos.Z );
labelpos.Y = g_Terrain.getExactGroundLevel( labelpos.X, labelpos.Z );
#endif
g_Camera.GetScreenCoordinates( labelpos, x, y );
glColor4f( 1.0f, 1.0f, 1.0f, it->fade );
@ -672,15 +679,28 @@ int interactInputHandler( const SDL_Event* ev )
static bool button_down = false;
switch( ev->type )
{
case SDL_KEYDOWN:
if( ( ev->key.keysym.sym >= SDLK_0 ) && ( ev->key.keysym.sym <= SDLK_9 ) )
{
case SDL_HOTKEYDOWN:
switch( ev->user.code )
{
u8 id = ev->key.keysym.sym - SDLK_0;
// This competes with the camera bookmarks for the top-row number keys
// Find out which this is, and act accordingly
if( !hotkeys[HOTKEY_CAMERA_BOOKMARK_MODIFIER] )
case HOTKEY_HIGHLIGHTALL:
g_Mouseover.m_viewall = true;
break;
case HOTKEY_SELECTION_SNAP:
if( g_Selection.m_selected.size() )
setCameraTarget( g_Selection.getSelectionPosition() );
break;
case HOTKEY_CONTEXTORDER_NEXT:
g_Selection.nextContext();
break;
case HOTKEY_CONTEXTORDER_PREVIOUS:
g_Selection.previousContext();
break;
default:
if( ( ev->user.code >= HOTKEY_SELECTION_GROUP_0 ) && ( ev->key.keysym.sym <= HOTKEY_SELECTION_GROUP_19 ) )
{
u8 id = ev->user.code - HOTKEY_SELECTION_GROUP_0;
if( hotkeys[HOTKEY_SELECTION_GROUP_ADD] )
{
g_Selection.addGroup( id );
@ -702,13 +722,15 @@ int interactInputHandler( const SDL_Event* ev )
else
g_Selection.loadGroup( id );
}
return( EV_HANDLED );
}
else
else if( ( ev->user.code >= HOTKEY_CAMERA_BOOKMARK_0 ) && ( ev->user.code <= HOTKEY_CAMERA_BOOKMARK_9 ) )
{
u8 id = ev->user.code - HOTKEY_CAMERA_BOOKMARK_0;
if( hotkeys[HOTKEY_CAMERA_BOOKMARK_SAVE] )
{
// Attempt to track the ground we're looking at
CHFTracer tracer( g_Terrain.GetHeightMap(), g_Terrain.GetVerticesPerSide(), CELL_SIZE, HEIGHT_SCALE ); int x, z;
CHFTracer tracer( g_Terrain.GetHeightMap(), g_Terrain.GetVerticesPerSide(), (float)CELL_SIZE, (float)HEIGHT_SCALE ); int x, z;
CVector3D origin, dir, delta, currentTarget;
origin = g_Camera.m_Orientation.GetTranslation();
dir = g_Camera.m_Orientation.GetIn();
@ -736,26 +758,8 @@ int interactInputHandler( const SDL_Event* ev )
if( bookmarkInUse[id] )
setCameraTarget( cameraBookmarks[id] );
}
return( EV_HANDLED );
}
}
break;
case SDL_HOTKEYDOWN:
switch( ev->user.code )
{
case HOTKEY_HIGHLIGHTALL:
g_Mouseover.m_viewall = true;
break;
case HOTKEY_SELECTION_SNAP:
if( g_Selection.m_selected.size() )
setCameraTarget( g_Selection.getSelectionPosition() );
break;
case HOTKEY_CONTEXTORDER_NEXT:
g_Selection.nextContext();
break;
case HOTKEY_CONTEXTORDER_PREVIOUS:
g_Selection.previousContext();
break;
default:
return( EV_PASS );
}
return( EV_HANDLED );
@ -879,7 +883,7 @@ void setCameraTarget( const CVector3D& target )
// the difference beteen that position and the camera point, and restoring
// that difference to our new target)
CHFTracer tracer( g_Terrain.GetHeightMap(), g_Terrain.GetVerticesPerSide(), CELL_SIZE, HEIGHT_SCALE ); int x, z;
CHFTracer tracer( g_Terrain.GetHeightMap(), g_Terrain.GetVerticesPerSide(), (float)CELL_SIZE, (float)HEIGHT_SCALE ); int x, z;
CVector3D origin, dir, currentTarget;
origin = g_Camera.m_Orientation.GetTranslation();
dir = g_Camera.m_Orientation.GetIn();

View File

@ -14,7 +14,7 @@ class CVector2D
public:
float x;
float y;
CVector2D() {}
CVector2D() { x = 0.0f; y = 0.0f; }
CVector2D( float _x, float _y )
{
x = _x; y = _y;

View File

@ -106,7 +106,7 @@ JSBool JSI_Selection::isValidContextOrder( JSContext* context, JSObject* obj, un
JSBool JSI_Selection::getContextOrder( JSContext* context, JSObject* obj, jsval id, jsval* vp )
{
*vp = g_Selection.m_contextOrder;
*vp = INT_TO_JSVAL( g_Selection.m_contextOrder );
return( JS_TRUE );
}

View File

@ -312,13 +312,13 @@ void ScriptingHost::ErrorReporter(JSContext * context, const char * message, JSE
if (g_Console)
{
g_Console->InsertMessage( L"%hs (%d)", report->filename, report->lineno );
if (message)
{
g_Console->InsertMessage( L"%hs", message );
g_Console->InsertMessage( L"JavaScript Error (%hs, line %d): %hs", report->filename, report->lineno, message );
}
else
g_Console->InsertMessage( L"No error message available" );
g_Console->InsertMessage( L"JavaScript Error (%hs, line %d): No error message available", report->filename, report->lineno );
}
if (report->filename != NULL)

View File

@ -48,7 +48,7 @@ bool CBaseEntity::loadXML( CStr filename )
EL(turningradius);
EL(size);
EL(footprint);
EL(boundsoffset);
EL(graphicsoffset);
AT(radius);
AT(width);
AT(height);
@ -103,21 +103,13 @@ bool CBaseEntity::loadXML( CStr filename )
m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() );
m_bound_type = CBoundingObject::BOUND_OABB;
}
else if (ChildName == el_boundsoffset)
else if (ChildName == el_graphicsoffset)
{
CStr x (Child.getAttributes().getNamedItem(at_x));
CStr y (Child.getAttributes().getNamedItem(at_y));
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
if( !m_bound_box )
m_bound_box = new CBoundingBox();
m_bound_circle->m_offset.x = x.ToFloat();
m_bound_circle->m_offset.y = y.ToFloat();
m_bound_box->m_offset.x = x.ToFloat();
m_bound_box->m_offset.y = y.ToFloat();
m_graphicsOffset.x = x.ToFloat();
m_graphicsOffset.y = y.ToFloat();
}
}

View File

@ -40,6 +40,7 @@ public:
CBoundingCircle* m_bound_circle;
CBoundingBox* m_bound_box;
CBoundingObject::EBoundingType m_bound_type;
CVector2D m_graphicsOffset;
CProperty_float m_speed;
CProperty_float m_turningRadius;

View File

@ -40,7 +40,6 @@ CBoundingCircle::CBoundingCircle( float x, float y, float radius )
CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy )
{
m_type = BOUND_CIRCLE;
m_offset = copy->m_offset;
setPosition( x, y );
setRadius( copy->m_radius );
}
@ -48,7 +47,6 @@ CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy )
void CBoundingObject::setPosition( float x, float y )
{
m_pos.x = x; m_pos.y = y;
m_pos += m_offset;
}
void CBoundingCircle::setRadius( float radius )
@ -95,7 +93,6 @@ CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, float width, f
CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, CBoundingBox* copy )
{
m_type = BOUND_OABB;
m_offset = copy->m_offset;
setPosition( x, y );
setDimensions( copy->getWidth(), copy->getHeight() );
setOrientation( u );
@ -112,7 +109,6 @@ CBoundingBox::CBoundingBox( float x, float y, float orientation, float width, fl
CBoundingBox::CBoundingBox( float x, float y, float orientation, CBoundingBox* copy )
{
m_type = BOUND_OABB;
m_offset = copy->m_offset;
setPosition( x, y );
setDimensions( copy->getWidth(), copy->getHeight() );
setOrientation( orientation );

View File

@ -18,7 +18,7 @@ class CBoundingCircle;
class CBoundingObject
{
public:
CBoundingObject() { m_offset.x = 0; m_offset.y = 0; }
CBoundingObject() {}
enum EBoundingType
{
BOUND_CIRCLE,
@ -26,7 +26,6 @@ public:
};
EBoundingType m_type;
CVector2D m_pos;
CVector2D m_offset;
float m_radius;
void setPosition( float x, float y );
bool intersects( CBoundingObject* obj );

View File

@ -23,6 +23,27 @@ CBoundingObject* getContainingObject( const CVector2D& point )
return( NULL );
}
CBoundingObject* getCollisionObject( CBoundingObject* bounds )
{
std::vector<HEntity>* entities = g_EntityManager.getExtant();
std::vector<HEntity>::iterator it;
for( it = entities->begin(); it != entities->end(); it++ )
{
assert( (*it)->m_bounds );
if( (*it)->m_bounds == bounds ) continue;
if( bounds->intersects( (*it)->m_bounds ) )
{
CBoundingObject* obj = (*it)->m_bounds;
delete( entities );
return( obj );
}
}
delete( entities );
return( NULL );
}
HEntity getCollisionObject( CEntity* entity )
{
assert( entity->m_bounds );

View File

@ -29,6 +29,7 @@ struct rayIntersectionResults
HEntity getCollisionObject( CEntity* entity );
HEntity getCollisionObject( CEntity* entity, float x, float y );
CBoundingObject* getCollisionObject( CBoundingObject* bounds );
CBoundingObject* getContainingObject( const CVector2D& point );
bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, CBoundingObject* destinationCollisionObject, rayIntersectionResults* results );

View File

@ -20,7 +20,7 @@ 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 );
@ -41,8 +41,18 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
m_base = base;
loadBase();
// Nasty hackish correction for models that are off-centre.
// Bad artist. No cookie.
m_position.X += m_graphicsOffset.x;
m_position.Z += m_graphicsOffset.y;
if( m_bounds )
m_bounds->setPosition( m_position.X, m_position.Z );
// We can now freely admit that this exists.
m_position_previous = m_position;
m_orientation_previous = m_orientation;
m_extant = true;
m_extant_mirror = true;
@ -88,6 +98,8 @@ void CEntity::loadBase()
m_base->m_inheritors.push_back( this );
rebuild();
m_graphicsOffset = m_base->m_graphicsOffset;
if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
{
m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle );
@ -96,6 +108,9 @@ void CEntity::loadBase()
{
m_bounds = new CBoundingBox( m_position.X, m_position.Z, m_ahead, m_base->m_bound_box );
}
}
void CEntity::kill()
@ -130,65 +145,17 @@ void CEntity::updateActorTransforms()
float s = sin( m_graphics_orientation );
float c = cos( m_graphics_orientation );
m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = m_graphics_position.X;
m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = m_graphics_position.X - m_graphicsOffset.x;
m._21 = 0.0f; m._22 = 1.0f; m._23 = 0.0f; m._24 = m_graphics_position.Y;
m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = m_graphics_position.Z;
m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = m_graphics_position.Z - m_graphicsOffset.y;
m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f;
m_actor->GetModel()->SetTransform( m );
}
float CEntity::getExactGroundLevel( float x, float y )
{
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_graphics_position.Y = getExactGroundLevel( m_graphics_position.X, m_graphics_position.Z );
m_graphics_position.Y = g_Terrain.getExactGroundLevel( m_graphics_position.X, m_graphics_position.Z );
}
void CEntity::update( float timestep )
@ -417,11 +384,11 @@ void CEntity::render()
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 );
glVertex3f( x0 + fwd.x * r.distance, g_Terrain.getExactGroundLevel( x0 + fwd.x * r.distance, y0 + fwd.y * r.distance ) + 0.25f, y0 + fwd.y * r.distance );
glVertex3f( r.position.x, g_Terrain.getExactGroundLevel( r.position.x, r.position.y ) + 0.25f, r.position.y );
glEnd();
glBegin( GL_LINE_STRIP );
glVertex3f( x0, getExactGroundLevel( x0, y0 ), y0 );
glVertex3f( x0, g_Terrain.getExactGroundLevel( x0, y0 ), y0 );
}
switch( it->m_type )
{
@ -438,7 +405,7 @@ void CEntity::render()
continue;
}
glVertex3f( x, getExactGroundLevel( x, y ) + 0.25f, y );
glVertex3f( x, g_Terrain.getExactGroundLevel( x, y ) + 0.25f, y );
}
glEnd();
@ -447,7 +414,7 @@ void CEntity::render()
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 );
m_bounds->render( g_Terrain.getExactGroundLevel( m_position.X, m_position.Z ) + 0.25f ); //m_position.Y + 0.25f );
}
void CEntity::renderSelectionOutline( float alpha )
@ -455,12 +422,11 @@ void CEntity::renderSelectionOutline( float alpha )
if( !m_bounds ) return;
glColor4f( 1.0f, 1.0f, 1.0f, alpha );
if( getCollisionObject( this ) ) glColor4f( 1.0f, 0.5f, 0.5f, alpha );
glBegin( GL_LINE_LOOP );
CVector3D pos = m_graphics_position;
pos.X += m_bounds->m_offset.x;
pos.Z += m_bounds->m_offset.y;
switch( m_bounds->m_type )
{
@ -473,7 +439,7 @@ void CEntity::renderSelectionOutline( float alpha )
float x = pos.X + radius * sin( ang );
float y = pos.Z + radius * cos( ang );
#ifdef SELECTION_TERRAIN_CONFORMANCE
glVertex3f( x, getExactGroundLevel( x, y ) + 0.25f, y );
glVertex3f( x, g_Terrain.getExactGroundLevel( x, y ) + 0.25f, y );
#else
glVertex3f( x, pos.Y + 0.25f, y );
#endif
@ -497,29 +463,29 @@ void CEntity::renderSelectionOutline( float alpha )
for( int i = SELECTION_BOX_POINTS; i > -SELECTION_BOX_POINTS; i-- )
{
p = q + u * h + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
glVertex3f( p.x, getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
glVertex3f( p.x, g_Terrain.getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = SELECTION_BOX_POINTS; i > -SELECTION_BOX_POINTS; i-- )
{
p = q + u * ( h * (float)i / (float)SELECTION_BOX_POINTS ) - v * w;
glVertex3f( p.x, getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
glVertex3f( p.x, g_Terrain.getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = -SELECTION_BOX_POINTS; i < SELECTION_BOX_POINTS; i++ )
{
p = q - u * h + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
glVertex3f( p.x, getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
glVertex3f( p.x, g_Terrain.getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = -SELECTION_BOX_POINTS; i < SELECTION_BOX_POINTS; i++ )
{
p = q + u * ( h * (float)i / (float)SELECTION_BOX_POINTS ) + v * w;
glVertex3f( p.x, getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
glVertex3f( p.x, g_Terrain.getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
#else
p = q + u * h + v * w;
glVertex3f( p.x, getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
glVertex3f( p.x, g_Terrain.getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
p = q + u * h - v * w;
glVertex3f( p.x, getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );

View File

@ -61,12 +61,13 @@ public:
u8 m_grouped;
CProperty_i32 m_grouped_mirror;
//-- Interpolated property
CVector3D m_position;
CVector3D m_position_previous;
CProperty_CVector3D m_graphics_position;
CVector2D m_graphicsOffset;
CBoundingObject* m_bounds;
float m_targetorientation;
CVector2D m_ahead;
@ -98,7 +99,6 @@ public:
void kill();
void interpolate( float relativeoffset );
float getExactGroundLevel( float x, float y );
void snapToGround();
void updateActorTransforms();
void render();

View File

@ -47,7 +47,10 @@ bool HEntity::operator ==( const HEntity& test ) const
void HEntity::addRef()
{
if( m_handle != INVALID_HANDLE )
{
assert( m_handle < 4096 );
g_EntityManager.m_entities[m_handle].m_refcount++;
}
}
void HEntity::decRef()

View File

@ -6,7 +6,7 @@
int SELECTION_CIRCLE_POINTS;
int SELECTION_BOX_POINTS;
int SELECTION_SMOOTHNESS_UNIFIED;
int SELECTION_SMOOTHNESS_UNIFIED = 9;
CEntityManager::CEntityManager()
{

View File

@ -7,6 +7,9 @@
#include "Collision.h"
#include "PathfindEngine.h"
#include "Terrain.h"
extern CTerrain g_Terrain;
bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
{
@ -43,6 +46,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
// making the paths we calculate useless.
// It's also painful trying to watch two entities resolve their
// collision when they're both bound by turning constraints.
m_ahead = delta / len;
m_orientation = atan2( m_ahead.x, m_ahead.y );
}
@ -84,42 +88,75 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
delta = m_ahead * scale;
// What would happen if we moved forward a little?
m_position.X += delta.x;
m_position.Z += delta.y;
m_bounds->setPosition( m_position.X, m_position.Z );
HEntity collide = getCollisionObject( this );
if( collide )
{
// Hit something. Is it our destination?
// We'd hit something. Let's not.
m_position.X -= delta.x;
m_position.Z -= delta.y;
m_bounds->m_pos -= delta;
// Is it too late to avoid the collision?
if( collide->m_bounds->intersects( m_bounds ) )
{
// Yes. Oh dear. That can't be good.
// This really shouldn't happen in the current build.
assert( false && "Overlapping objects" );
// Erm... do nothing?
return( false );
}
// No. Is our destination within the obstacle?
if( collide->m_bounds->contains( current->m_data[0].location ) )
{
// Yes? All well and good, then. Stop here.
m_orderQueue.pop_front();
return( false );
}
// No? Take a step back.
m_position.X -= delta.x;
m_position.Z -= delta.y;
// No. Are we nearing our destination, do we wish to stop there, and is it obstructed?
m_bounds->setPosition( m_position.X, m_position.Z );
// Are we still hitting it?
if( collide->m_bounds->intersects( m_bounds ) )
if( ( m_orderQueue.size() == 1 ) && ( len <= 10.0f ) )
{
// Oh dear. Most likely explanation is that this unit was created
// within the bounding area of another entity.
// Try a little boost of speed, to help resolve the situation more quickly.
CBoundingCircle destinationObs( current->m_data[0].location.x, current->m_data[0].location.y, m_bounds->m_radius );
if( getCollisionObject( &destinationObs ) )
{
// Yes. (Chances are a bunch of units were tasked to the same destination)
// Here's a wierd idea: (I hope it works)
// Spiral round the destination until a free point is found.
float r = 0.0f, theta = 0.0f, delta;
float interval = destinationObs.m_radius;
float _x = current->m_data[0].location.x, _y = current->m_data[0].location.y;
while( true )
{
delta = interval / r;
theta += delta;
r += ( interval * delta ) / ( 2 * PI );
destinationObs.setPosition( _x + r * cosf( theta ), _y + r * sinf( theta ) );
if( !getCollisionObject( &destinationObs ) ) break;
}
// Reset our destination
current->m_data[0].location.x = _x;
current->m_data[0].location.y = _y;
// This really shouldn't happen in the current build.
m_position.X += delta.x * 2.0f;
m_position.Z += delta.y * 2.0f;
m_bounds->setPosition( m_position.X, m_position.Z );
return( false );
return( false );
}
}
// No? Path around it.
CEntityOrder avoidance;
@ -128,6 +165,9 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
right.x = m_ahead.y; right.y = -m_ahead.x;
CVector2D avoidancePosition;
// Which is the shortest diversion, going left or right?
// (Weight a little towards the right, to stop both units dodging the same way)
if( ( collide->m_bounds->m_pos - m_bounds->m_pos ).dot( right ) < 1 )
{
// Turn right.
@ -139,6 +179,8 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
avoidancePosition = collide->m_bounds->m_pos - right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
}
// Create a short path representing this detour
avoidance.m_data[0].location = avoidancePosition;
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
m_orderQueue.pop_front();
@ -147,6 +189,27 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
}
// Will we step off the map?
if( !g_Terrain.isOnMap( m_position.X, m_position.Z ) )
{
// Yes. That's not a particularly good idea, either.
m_position.X -= delta.x;
m_position.Z -= delta.y;
m_bounds->setPosition( m_position.X, m_position.Z );
// All things being equal, we should only get here while on a collision path
// (No destination could be off the map)
assert( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION );
// Just stop here, repath if necessary.
m_orderQueue.pop_front();
}
// No. I suppose it's OK to go there, then. *disappointed*
return( false );
}
@ -155,13 +218,19 @@ bool CEntity::processGoto( CEntityOrder* current, float timestep )
CVector2D pos( m_position.X, m_position.Z );
CVector2D path_to = current->m_data[0].location;
m_orderQueue.pop_front();
// Let's just check we're going somewhere...
if( ( path_to - pos ).length() < 0.1f )
return( false );
if( m_actor->GetModel()->GetAnimation() != m_actor->GetObject()->m_WalkAnim )
{
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
}
// The pathfinder will push its result back into this unit's queue.
g_Pathfinder.requestPath( me, path_to );
return( true );
}
@ -170,6 +239,11 @@ bool CEntity::processPatrol( CEntityOrder* current, float timestep )
{
CEntityOrder this_segment;
CEntityOrder repeat_patrol;
// Duplicate the patrol order, push one copy onto the start of our order queue
// (that's the path we'll be taking next) and one copy onto the end of the
// queue (to keep us patrolling)
this_segment.m_type = CEntityOrder::ORDER_GOTO;
this_segment.m_data[0] = current->m_data[0];
repeat_patrol.m_type = CEntityOrder::ORDER_PATROL;

View File

@ -2,6 +2,14 @@
#include "PathfindEngine.h"
#include "PathfindSparse.h"
#include "ConfigDB.h"
CPathfindEngine::CPathfindEngine()
{
CConfigValue* sparseDepth = g_ConfigDB.GetValue( CFG_SYSTEM, "pathfind.sparse.recursiondepth" );
if( sparseDepth )
sparseDepth->GetInt( SPF_RECURSION_DEPTH );
}
void CPathfindEngine::requestPath( HEntity entity, const CVector2D& destination )
{

View File

@ -20,6 +20,7 @@
class CPathfindEngine : public Singleton<CPathfindEngine>
{
public:
CPathfindEngine();
void requestPath( HEntity entity, const CVector2D& destination );
};

View File

@ -1,10 +1,14 @@
#include "precompiled.h"
#include "PathfindSparse.h"
#include "Terrain.h"
sparsePathTree::sparsePathTree( const CVector2D& _from, const CVector2D& _to, HEntity _entity, CBoundingObject* _destinationCollisionObject )
int SPF_RECURSION_DEPTH = 10;
sparsePathTree::sparsePathTree( const CVector2D& _from, const CVector2D& _to, HEntity _entity, CBoundingObject* _destinationCollisionObject, int _recursionDepth )
{
from = _from; to = _to;
recursionDepth = _recursionDepth;
entity = _entity; destinationCollisionObject = _destinationCollisionObject;
leftPre = NULL; leftPost = NULL;
@ -26,6 +30,13 @@ bool sparsePathTree::slice()
{
if( type == SPF_OPEN_UNVISITED )
{
if( !recursionDepth )
{
// Too deep.
type = SPF_IMPOSSIBLE;
return( true );
}
rayIntersectionResults r;
CVector2D forward = to - from;
@ -75,15 +86,16 @@ bool sparsePathTree::slice()
if( r.closestApproach < 0 )
favourLeft = true;
// First we path to the left...
leftPre = new sparsePathTree( from, left, entity, destinationCollisionObject );
leftPost = new sparsePathTree( left, to, entity, destinationCollisionObject );
leftPre = new sparsePathTree( from, left, entity, destinationCollisionObject, recursionDepth - 1 );
leftPost = new sparsePathTree( left, to, entity, destinationCollisionObject, recursionDepth - 1 );
// Then we path to the right...
rightPre = new sparsePathTree( from, right, entity, destinationCollisionObject );
rightPost = new sparsePathTree( right, to, entity, destinationCollisionObject );
rightPre = new sparsePathTree( from, right, entity, destinationCollisionObject, recursionDepth - 1 );
rightPost = new sparsePathTree( right, to, entity, destinationCollisionObject, recursionDepth - 1 );
// If anybody reaches this point and is thinking:
//
@ -91,6 +103,28 @@ bool sparsePathTree::slice()
//
// Let me know.
// Check that the subwaypoints are on the map...
if( !g_Terrain.isOnMap( left ) )
{
// Shut that path down
leftPre->type = SPF_IMPOSSIBLE;
leftPost->type = SPF_IMPOSSIBLE;
}
if( !g_Terrain.isOnMap( right ) )
{
// Shut that path down
rightPre->type = SPF_IMPOSSIBLE;
rightPost->type = SPF_IMPOSSIBLE;
}
if( ( leftPre->type == SPF_IMPOSSIBLE ) && ( rightPre->type == SPF_IMPOSSIBLE ) )
{
// It's unlikely, but I suppose it /might/ happen
type = SPF_IMPOSSIBLE;
return( true );
}
type = SPF_OPEN_PROCESSING;
return( true );
@ -193,10 +227,10 @@ void pathSparse( HEntity entity, CVector2D destination )
// Sanity check:
if( source.length() < 0.01f ) return;
sparsePathTree sparseEngine( source, destination, entity, getContainingObject( destination ) );
sparsePathTree sparseEngine( source, destination, entity, getContainingObject( destination ), SPF_RECURSION_DEPTH );
while( sparseEngine.type & sparsePathTree::SPF_OPEN ) sparseEngine.slice();
assert( sparseEngine.type & sparsePathTree::SPF_SOLVED ); // Shouldn't be any impossible cases yet.
// assert( sparseEngine.type & sparsePathTree::SPF_SOLVED ); // Shouldn't be any impossible cases yet.
if( sparseEngine.type & sparsePathTree::SPF_SOLVED )
{

View File

@ -38,6 +38,7 @@ struct sparsePathTree
SPF_OPEN = 4,
SPF_SOLVED = 2
} type;
int recursionDepth;
HEntity entity;
CBoundingObject* destinationCollisionObject;
CVector2D from;
@ -59,12 +60,14 @@ struct sparsePathTree
sparsePathTree* subtrees[4];
};
unsigned short nextSubtree;
sparsePathTree( const CVector2D& from, const CVector2D& to, HEntity entity, CBoundingObject* destinationCollisionObject );
sparsePathTree( const CVector2D& from, const CVector2D& to, HEntity entity, CBoundingObject* destinationCollisionObject, int _recursionDepth );
~sparsePathTree();
bool slice();
void pushResults( std::vector<CVector2D>& nodelist );
};
extern int SPF_RECURSION_DEPTH;
void nodeSmooth( HEntity entity, std::vector<CVector2D>& nodelist );
void pathSparse( HEntity entity, CVector2D destination );
bool pathSparseRecursive( HEntity entity, CVector2D from, CVector2D to, CBoundingObject* destinationCollisionObject );