Changed hotkey handling for groups and bookmarks; small pathfinding fixes.
This was SVN commit r809.
This commit is contained in:
parent
1a33a9afc3
commit
befae8627d
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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++ )
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
int SELECTION_CIRCLE_POINTS;
|
||||
int SELECTION_BOX_POINTS;
|
||||
int SELECTION_SMOOTHNESS_UNIFIED;
|
||||
int SELECTION_SMOOTHNESS_UNIFIED = 9;
|
||||
|
||||
CEntityManager::CEntityManager()
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -20,6 +20,7 @@
|
||||
class CPathfindEngine : public Singleton<CPathfindEngine>
|
||||
{
|
||||
public:
|
||||
CPathfindEngine();
|
||||
void requestPath( HEntity entity, const CVector2D& destination );
|
||||
};
|
||||
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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 );
|
||||
|
Loading…
Reference in New Issue
Block a user