D:/simple_rts/src/Level.cpp

Go to the documentation of this file.
00001 #include "Level.h"
00002 
00003 #include "Unit.h"
00004 #include "Building.h"
00005 #include "OgreColourValue.h"
00006 
00007 #include "OgreException.h"
00008 
00009 #include <math.h>
00010 #include <assert.h>
00011 #include <queue>
00012 
00013 using Ogre::ColourValue;
00014 using Ogre::Exception;
00015 using std::queue;
00016 
00017 
00018 template<> ASR::Level* Singleton<ASR::Level>::ms_Singleton = 0;
00019 
00020 
00021 namespace ASR
00022 {
00023         // ----------------------------------------------------------------------------
00024         Level* Level::getSingletonPtr(void)
00025         {
00026                 return ms_Singleton;
00027         }
00028         Level& Level::getSingleton(void)
00029         {  
00030                 assert( ms_Singleton );  return ( *ms_Singleton );  
00031         }
00032 
00033         // ----------------------------------------------------------------------------
00034         Level::Level()
00035         {
00036                 mOccupied.clear ();
00037                 mCellsWide = 0;
00038                 mCellsHigh = 0;
00039 
00040                 mCellWidth = 0.0f;
00041                 mCellHeight = 0.0f;
00042 
00043                 mWorldWidth = 0.0f;
00044                 mWorldHeight = 0.0f;
00045 
00046                 mPathManager = new AStarSearchManager<Level::LevelNode> ();
00047         }
00048 
00049 
00050         // ----------------------------------------------------------------------------
00051         Level::~Level(void)
00052         {
00053                 mOccupied.clear ();
00054                 removeAllTeams ();
00055 
00056                 delete mPathManager;
00057         }
00058 
00059 
00060         // ----------------------------------------------------------------------------
00061         Level::LevelNode* Level::getNode ( const Vector3& worldPos )
00062         {
00063                 assert ( mCellWidth > 0.0f );
00064                 assert ( mCellHeight > 0.0f );
00065 
00066                 assert ( worldPos.x >= 0.0f );
00067                 assert ( worldPos.z >= 0.0f );
00068 
00069                 int cellX = int(floor(worldPos.x / mCellWidth));
00070                 int cellY = int(floor(worldPos.z / mCellHeight ));
00071 
00072                 if ( cellX >= mCellsWide || cellY >= mCellsHigh )
00073                         throw Exception ( Exception::ERR_ITEM_NOT_FOUND, "Position is outside of the terrain bounds", "Level::getNode" );
00074 
00075                 return &mOccupied[cellX + cellY * mCellsWide];
00076         }
00077 
00078 
00079         // ----------------------------------------------------------------------------
00080         void Level::setDimensions ( int cellsWide, int cellsHigh )
00081         {
00082                 mCellsWide = cellsWide;
00083                 mCellsHigh = cellsHigh;
00084 
00085                 mCellWidth = mWorldWidth / (float)mCellsWide;
00086                 mCellHeight = mWorldHeight / (float)mCellsHigh;
00087         }
00088 
00089 
00090         // ----------------------------------------------------------------------------
00091         void Level::setWorldDimensions ( float worldWidth, float worldHeight )
00092         {
00093                 mWorldWidth = worldWidth;
00094                 mWorldHeight = worldHeight;
00095         }
00096 
00097 
00098         // ----------------------------------------------------------------------------
00099         void Level::setCellSize ( float width, float height )
00100         {
00101                 mCellWidth = width;
00102                 mCellHeight = height;
00103 
00104                 // There may be only a little bit of a cell at the edges
00105                 mCellsWide = int(ceil( mWorldWidth / mCellWidth ));
00106                 mCellsHigh = int(ceil( mWorldHeight / mCellHeight ));
00107         }
00108 
00109 
00110         // ----------------------------------------------------------------------------
00111         void Level::reset ()
00112         {
00113                 mOccupied.clear ();
00114                 //mOccupied.resize ( mCellsWide * mCellsHigh, false );
00115         }
00116 
00117 
00118         // ----------------------------------------------------------------------------
00119         void Level::rebuild ()
00120         {
00121                 mOccupied.clear ();
00122                 unsigned int Id = 0;
00123 
00124                 for ( int z = 0; z < mCellsHigh; z++ )
00125                 {
00126                         for ( int x = 0; x < mCellsWide; x++ )
00127                         {
00128                                 LevelNode node ( Vector3(x * mCellWidth, 0, z * mCellHeight ) );
00129                                 node.mId = Id++;
00130                                 mOccupied.push_back ( node );
00131                         }
00132                 }
00133 
00134                 // Assign neighbours now that the level is built
00135                 for ( int z = 0; z < mCellsHigh; z++ )
00136                 {
00137                         for ( int x = 0; x < mCellsWide; x++ )
00138                         {
00139                                 LevelNode& node = mOccupied[z * mCellsWide + x];
00140                                 node.mNumNeighbours = 0;
00141 
00142                                 // Use the tile above this one
00143                                 if ( z != 0 )
00144                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[(z - 1) * mCellsWide + x];
00145 
00146                                 // Use the tile below this one
00147                                 if ( z < (mCellsHigh - 1) )
00148                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[(z + 1) * mCellsWide + x];
00149 
00150                                 // Use the tile to the left of this one
00151                                 if ( x != 0 )
00152                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z ) * mCellsWide + (x - 1)];
00153 
00154                                 // Use the tile to the right of this one
00155                                 if ( x < (mCellsWide - 1) )
00156                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z ) * mCellsWide + (x + 1)];
00157 
00158                                 
00159                                 // Use the top-left tile
00160                                 if ( x > 0 && z > 0 )
00161                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z - 1 ) * mCellsWide + ( x - 1 )];
00162 
00163                                 // Use the top-right tile
00164                                 if ( x < (mCellsWide - 1) && z > 0 )
00165                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z - 1 ) * mCellsWide + ( x + 1 )];
00166 
00167                                 // Use the bottom-left tile
00168                                 if ( x > 0 && z < (mCellsHigh - 1) )
00169                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z + 1 ) * mCellsWide + ( x - 1 )];
00170 
00171                                 // Use the bottom-right tile
00172                                 if ( x < (mCellsWide - 1) && z < (mCellsHigh - 1) )
00173                                         node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z + 1 ) * mCellsWide + ( x + 1 )];
00174                                 
00175                         }
00176                 }
00177         }
00178 
00179 
00180         // ----------------------------------------------------------------------------
00181         Vector3 Level::getNodeWorldPosition ( int cellX, int cellZ )
00182         {
00183                 float worldPosX = cellX * mCellWidth;
00184                 float worldPosY = cellZ * mCellHeight;
00185 
00186                 return Vector3( worldPosX, 0.0f, worldPosY );
00187         }
00188 
00189 
00190         // ----------------------------------------------------------------------------
00191         // TEAM MANAGEMENT //
00192         // ----------------------------------------------------------------------------
00193 
00194         // ----------------------------------------------------------------------------
00195         Team* Level::createTeam ( const String& teamName, const ColourValue& teamColor )
00196         {
00197                 if ( _findTeam ( teamName ) != NULL )
00198                         throw Exception ( Exception::ERR_DUPLICATE_ITEM, "Team already exists", "Level::createTeam" );
00199 
00200                 Team* newTeam = new Team ( teamName, teamColor );
00201                 mTeams.push_back ( newTeam );
00202 
00203                 // TODO
00204                 //              Register an updater for the team
00205                 return newTeam;
00206         }
00207 
00208 
00209         // ----------------------------------------------------------------------------
00210         void Level::removeAllTeams ()
00211         {
00212                 TeamList::iterator iter = mTeams.begin();
00213                 while ( iter != mTeams.end() )
00214                 {
00215                         delete (*iter);
00216                         iter++;
00217                 }
00218 
00219                 mTeams.clear ();
00220         }
00221 
00222 
00223         // ----------------------------------------------------------------------------
00224         void Level::removeTeam ( const String& teamName )
00225         {
00226                 TeamList::iterator iter = mTeams.begin();
00227                 while ( iter != mTeams.end () )
00228                 {
00229                         if ( (*iter)->getName() == teamName )
00230                         {
00231                                 delete (*iter);
00232                                 mTeams.erase ( iter );
00233                                 return;
00234                         }
00235                         iter++;
00236                 }
00237 
00238                 throw Exception ( Exception::ERR_ITEM_NOT_FOUND, "Cannot find team", "Team::removeTeam" );
00239         }
00240 
00241 
00242         // ----------------------------------------------------------------------------
00243         void Level::removeTeam ( Team* team )
00244         {
00245                 TeamList::iterator iter = mTeams.begin();
00246                 while ( iter != mTeams.end() )
00247                 {
00248                         if ( (*iter) == team )
00249                         {
00250                                 delete (*iter);
00251                                 mTeams.erase ( iter );
00252                                 return;
00253                         }
00254                         iter++;
00255                 }
00256 
00257                 throw Exception ( Exception::ERR_ITEM_NOT_FOUND, "Cannot find team", "Team::removeTeam" );
00258         }
00259 
00260 
00261         // ----------------------------------------------------------------------------
00262         size_t Level::getNumTeams ()
00263         {
00264                 return mTeams.size();
00265         }
00266 
00267 
00268         // ----------------------------------------------------------------------------
00269         Team* Level::getTeam ( size_t teamIndex )
00270         {
00271                 assert ( teamIndex < mTeams.size() );
00272                 return mTeams[teamIndex];
00273         }
00274 
00275 
00276         // ----------------------------------------------------------------------------
00277         Level::TeamIterator Level::getTeamIterator ()
00278         {
00279                 return TeamIterator ( mTeams.begin(), mTeams.end() );
00280         }
00281 
00282 
00283         // ----------------------------------------------------------------------------
00284         Level::TeamList Level::getEnemyTeams ( const Team* friendlyTeam )
00285         {
00286                 TeamList enemyTeams;
00287 
00288                 TeamList::iterator baseTeam = find ( mTeams.begin(), mTeams.end(), friendlyTeam );
00289                 if ( baseTeam == mTeams.end() )
00290                         throw Exception ( Exception::ERR_ITEM_NOT_FOUND, "Couldn't find team", "Level::getEnemyTeams" );
00291 
00292                 TeamList::iterator curIter = mTeams.begin();
00293 
00294                 // Up to but not including the base team
00295                 while ( curIter != baseTeam )
00296                 {
00297                         enemyTeams.push_back ( *curIter );
00298                         curIter++;
00299                 }
00300 
00301                 // Now the other half of the list
00302                 curIter = baseTeam + 1;
00303                 while (curIter != mTeams.end() )
00304                 {
00305                         enemyTeams.push_back ( *curIter );
00306                         curIter++;
00307                 }
00308 
00309                 return enemyTeams;
00310         }
00311 
00312 
00313         // ----------------------------------------------------------------------------
00314         Team* Level::_findTeam ( const String& teamName ) const
00315         {
00316                 TeamList::const_iterator iter = mTeams.begin();
00317                 while ( iter != mTeams.end() )
00318                 {
00319                         if ( (*iter)->getName() == teamName )
00320                         {
00321                                 return ( *iter );
00322                         }
00323                         iter++;
00324                 }
00325 
00326                 return NULL;
00327         }
00328 
00329 
00330         // ----------------------------------------------------------------------------
00331         Level::UnitList Level::getAdjacentUnits ( const Vector3& position, float radius )
00332         {
00333                 UnitList units;
00334 
00335                 // TODO
00336                 //              Loop over each team
00337                 //                      Loop over each unit from each team
00338                 //                              if the unit is within 'radius' units of 'position'
00339                 //                              add it to the list
00340                 //
00341                 //              This can be done a lot more efficiently with a quadtree structure
00342                 return units;
00343         }
00344 
00345 
00346         // ----------------------------------------------------------------------------
00347         void Level::occupyRegion ( const AxisAlignedBox& worldRegion )
00348         {
00349                 // TODO
00350                 //              Possibly use only 90% of mCellHeight to account for floating point
00351                 //              errors which may end up making us skip a cell. With 90%, we have
00352                 //              overlap but at least we cover all the cells
00353                 for ( float worldZ = worldRegion.getMinimum ().z; worldZ < worldRegion.getMaximum ().z; worldZ += mCellHeight )
00354                 {
00355                         for ( float worldX = worldRegion.getMinimum ().x; worldX < worldRegion.getMaximum ().x; worldX += mCellWidth )
00356                         {
00357                                 _markCell ( worldX, worldZ, true );
00358                         }
00359                 }
00360         }
00361 
00362 
00363         // ----------------------------------------------------------------------------
00364         void Level::unoccupyRegion ( const AxisAlignedBox& worldRegion )
00365         {
00366                 // TODO
00367                 //              Possibly use only 90% of mCellHeight to account for floating point
00368                 //              errors which may end up making us skip a cell. With 90%, we have
00369                 //              overlap but at least we cover all the cells
00370                 float startX = (worldRegion.getMinimum ().x < 0.0f) ? 0.0f : worldRegion.getMinimum ().x;
00371                 float startZ = (worldRegion.getMinimum ().z < 0.0f) ? 0.0f : worldRegion.getMinimum ().z;
00372 
00373                 for ( float worldZ = startZ; worldZ < worldRegion.getMaximum ().z; worldZ += mCellHeight )
00374                 {
00375                         for ( float worldX = startX; worldX < worldRegion.getMaximum ().x; worldX += mCellWidth )
00376                         {
00377                                 _markCell ( worldX, worldZ, false );
00378                         }
00379                 }
00380         }
00381 
00382 
00383         // ----------------------------------------------------------------------------
00384         void Level::_markCell ( float worldX, float worldZ, bool status )
00385         {
00386                 assert ( mCellWidth > 0.0f );
00387                 assert ( mCellHeight > 0.0f );
00388 
00389                 assert ( worldX >= 0.0f );
00390                 assert ( worldZ >= 0.0f );
00391 
00392                 int cellX;
00393                 int cellY;
00394 
00395                 cellX = int(floor(worldX / mCellWidth));
00396                 cellY = int(floor(worldZ / mCellHeight ));
00397 
00398                 if ( cellX >= mCellsWide || cellY >= mCellsHigh )
00399                         throw Exception ( Exception::ERR_ITEM_NOT_FOUND, "Position is outside of the bounds of the terrain", "Level::_markCell" );
00400 
00401                 float cost;
00402                 cost = ( status ) ? -1.0f : 1.0f;
00403                 mOccupied[cellX + cellY * mCellsWide].setTraversalCost ( cost );
00404         }
00405 
00406 
00407         // ----------------------------------------------------------------------------
00408         Level::LevelNode* Level::getClosestNode ( const Vector3& worldPos )
00409         {
00410                 assert ( mCellWidth > 0.0f );
00411                 assert ( mCellHeight > 0.0f );
00412 
00413                 assert ( worldPos.x >= 0.0f );
00414                 assert ( worldPos.z >= 0.0f );
00415 
00416                 int cellX = int(floor(worldPos.x / mCellWidth));
00417                 int cellY = int(floor(worldPos.z / mCellHeight ));
00418 
00419                 if ( cellX >= mCellsWide || cellY >= mCellsHigh )
00420                         throw Exception ( Exception::ERR_ITEM_NOT_FOUND, "Position is out of the terrain bounds", "Level::getClosestNode" );
00421 
00422                 if ( mOccupied[cellX + cellY * mCellsWide].getTraversalCost () >= 0.0f )
00423                         return &mOccupied[cellX + cellY * mCellsWide];
00424 
00425                 // Perform a breadth-first search to find the closest cell
00426                 std::queue<Level::LevelNode*> open;
00427                 mOccupied[cellX + cellY * mCellsWide].mChecked = true;
00428                 open.push ( &mOccupied[cellX + cellY * mCellsWide] );
00429 
00430                 // TODO
00431                 //              Come up with a better way other than resetting every single node
00432                 //              in the entire level
00433                 for ( int z = 0; z < mCellsHigh; z++ )
00434                 {
00435                         for ( int x = 0; x < mCellsWide; x++ )
00436                         {
00437                                 mOccupied[x + z * mCellsWide].mChecked = false;
00438                         }
00439                 }
00440 
00441                 Level::LevelNode* node;
00442                 Level::LevelNode* curNeighbour;
00443                 while ( !open.empty() )
00444                 {
00445                         node = open.front();
00446                         open.pop();
00447                         for ( int i = 0; i < node->getNumNeighbours (); i++ )
00448                         {
00449                                 curNeighbour = node->getNeighbour (i);
00450                                 if ( !curNeighbour->mChecked )
00451                                 {
00452                                         // We found a neighbour that we can use
00453                                         if ( curNeighbour->getTraversalCost () >= 0.0f )
00454                                         {
00455                                                 return curNeighbour;
00456                                         }
00457                                         
00458                                         curNeighbour->mChecked = true;
00459                                         open.push ( curNeighbour );
00460                                 }
00461                         }
00462                 }
00463 
00464                 throw Exception ( Exception::ERR_INTERNAL_ERROR, "Cannot find a valid node in the entire level", "Level::getClosestNode" );
00465         }
00466 
00467 
00468         // ----------------------------------------------------------------------------
00469         void Level::getCellLocation (const Vector3& worldPos, int* x, int* z )
00470         {
00471                 (*x) = int(floor(worldPos.x / mCellWidth));
00472                 (*z) = int(floor(worldPos.z / mCellHeight ));
00473 
00474                 if ( (*x) >= mCellsWide || (*z) >= mCellsHigh || (*z) < 0 || (*x) < 0 )
00475                         throw Exception ( Exception::ERR_ITEM_NOT_FOUND, "Position is outside of the bounds of the terrain", "Level::getCellLocation" );
00476         }
00477 
00478 
00479         // ----------------------------------------------------------------------------
00480         int Level::getWorldWidth () const
00481         {
00482                 return mWorldWidth;
00483         }
00484 
00485 
00486         // ----------------------------------------------------------------------------
00487         int Level::getWorldHeight () const
00488         {
00489                 return mWorldHeight;
00490         }
00491 
00492 
00493         // ----------------------------------------------------------------------------
00494         int Level::getNumNodesWide () const
00495         {
00496                 return mCellsWide;
00497         }
00498 
00499 
00500         // ----------------------------------------------------------------------------
00501         int Level::getNumNodesHigh () const
00502         {
00503                 return mCellsHigh;
00504         }
00505 
00506 
00507         // ----------------------------------------------------------------------------
00508         AStarSearchManager<Level::LevelNode>* Level::getPathManager () const
00509         {
00510                 return mPathManager;
00511         }
00512 }

Generated on Sun Jun 25 19:23:43 2006 for Valors End by  doxygen 1.4.7