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
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
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
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
00143 if ( z != 0 )
00144 node.mNeighbours[node.mNumNeighbours++] = &mOccupied[(z - 1) * mCellsWide + x];
00145
00146
00147 if ( z < (mCellsHigh - 1) )
00148 node.mNeighbours[node.mNumNeighbours++] = &mOccupied[(z + 1) * mCellsWide + x];
00149
00150
00151 if ( x != 0 )
00152 node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z ) * mCellsWide + (x - 1)];
00153
00154
00155 if ( x < (mCellsWide - 1) )
00156 node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z ) * mCellsWide + (x + 1)];
00157
00158
00159
00160 if ( x > 0 && z > 0 )
00161 node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z - 1 ) * mCellsWide + ( x - 1 )];
00162
00163
00164 if ( x < (mCellsWide - 1) && z > 0 )
00165 node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z - 1 ) * mCellsWide + ( x + 1 )];
00166
00167
00168 if ( x > 0 && z < (mCellsHigh - 1) )
00169 node.mNeighbours[node.mNumNeighbours++] = &mOccupied[( z + 1 ) * mCellsWide + ( x - 1 )];
00170
00171
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
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
00204
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
00295 while ( curIter != baseTeam )
00296 {
00297 enemyTeams.push_back ( *curIter );
00298 curIter++;
00299 }
00300
00301
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
00336
00337
00338
00339
00340
00341
00342 return units;
00343 }
00344
00345
00346
00347 void Level::occupyRegion ( const AxisAlignedBox& worldRegion )
00348 {
00349
00350
00351
00352
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
00367
00368
00369
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
00426 std::queue<Level::LevelNode*> open;
00427 mOccupied[cellX + cellY * mCellsWide].mChecked = true;
00428 open.push ( &mOccupied[cellX + cellY * mCellsWide] );
00429
00430
00431
00432
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
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 }