00001
00002
00003
00004
00005
00006
00007
00008
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <algorithm>
00023
00024 #include "vehicletype.h"
00025 #include "buildingtype.h"
00026 #include "graphicset.h"
00027 #include "gameoptions.h"
00028 #include "textfiletags.h"
00029 #include "stringtokenizer.h"
00030 #include "textfile_evaluation.h"
00031 #include "graphics/surface.h"
00032
00033 #include "errors.h"
00034 #include "sgstream.h"
00035 #include "graphics/blitter.h"
00036 #include "graphics/ColorTransform_PlayerColor.h"
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068 BuildingType :: BuildingType ( void )
00069 {
00070 for ( int x = 0; x < 4; x++ )
00071 for ( int y = 0; y < 6; y++ ) {
00072 field_Exists[x][y] = false;
00073 for ( int w = 0; w < cwettertypennum; ++w )
00074 for ( int c = 0; c < maxbuildingpicnum; ++c )
00075 bi_picture[w][c][x][y] = -1;
00076 }
00077
00078 buildingNotRemovable = false;
00079
00080 static const float matrix[] = { 1, 0, 0,
00081 0, 1, 0,
00082 0, 0, 1 };
00083
00084 productionEfficiency = ResourceMatrix( matrix );
00085
00086 }
00087
00088
00089 int BuildingType :: getMemoryFootprint() const
00090 {
00091 int size = sizeof(*this);
00092
00093 for ( int i = 0; i < cwettertypennum; ++i )
00094 for ( int j = 0; j < maxbuildingpicnum; ++j )
00095 for ( int k = 0; k < 4; ++k )
00096 for ( int l = 0; l < 6; ++l )
00097 size += w_picture[i][j][k][l].getMemoryFootprint() - sizeof( Surface );
00098 return size;
00099 }
00100
00101
00102 int BuildingType :: getBIPicture( const LocalCoordinate& localCoordinate, int weather , int constructionStep ) const
00103 {
00104 return bi_picture [weather][constructionStep][localCoordinate.x][localCoordinate.y];
00105 }
00106
00107
00108 const Surface& BuildingType :: getPicture ( const LocalCoordinate& localCoordinate, int weather, int constructionStep ) const
00109 {
00110 if( constructionStep >= construction_steps )
00111 constructionStep = construction_steps-1;
00112
00113 if ( !weatherBits.test(weather))
00114 weather = 0;
00115
00116 if ( bi_picture [weather][constructionStep][localCoordinate.x][localCoordinate.y] <= 0 )
00117 return w_picture[weather][constructionStep][localCoordinate.x][localCoordinate.y];
00118 else
00119 return GraphicSetManager::Instance().getPic(bi_picture [weather][constructionStep][localCoordinate.x][localCoordinate.y]);
00120
00121 }
00122
00123 void BuildingType::paint ( Surface& s, SPoint pos, const PlayerColor& player, int weather, int constructionStep ) const
00124 {
00125 for ( int x = 0; x < 4; x++ )
00126 for ( int y = 0; y < 6; y++ )
00127 if ( fieldExists(LocalCoordinate(x,y) ))
00128 paintSingleField(s,pos,LocalCoordinate(x,y),player,weather,constructionStep);
00129
00130 }
00131
00132 void BuildingType::paint ( Surface& s, SPoint pos ) const
00133 {
00134 for ( int x = 0; x < 4; x++ )
00135 for ( int y = 0; y < 6; y++ )
00136 if ( fieldExists(LocalCoordinate(x,y) ))
00137 paintSingleField(s,pos,LocalCoordinate(x,y));
00138
00139 }
00140
00141
00142 void BuildingType:: paintSingleField ( Surface& s, SPoint pos, const LocalCoordinate& localCoordinate, const PlayerColor& player, int weather, int constructionStep ) const
00143 {
00144 megaBlitter<ColorTransform_PlayerCol,
00145 ColorMerger_AlphaOverwrite,
00146 SourcePixelSelector_Plain,
00147 TargetPixelSelector_All>
00148 ( getPicture(localCoordinate,weather,constructionStep),
00149 s,
00150 SPoint( pos.x + localCoordinate.x * fielddistx + ( localCoordinate.y & 1 ) * fielddisthalfx, pos.y + localCoordinate.y * fielddisty),
00151 player,
00152 nullParam, nullParam, nullParam );
00153
00154
00155 }
00156
00157 void BuildingType:: paintSingleField ( Surface& s, SPoint pos, const LocalCoordinate& localCoordinate, int weather, int constructionStep ) const
00158 {
00159 megaBlitter<ColorTransform_None,
00160 ColorMerger_AlphaOverwrite,
00161 SourcePixelSelector_Plain,
00162 TargetPixelSelector_All>
00163 ( getPicture(localCoordinate,weather,constructionStep),
00164 s,
00165 SPoint( pos.x + localCoordinate.x * fielddistx + ( localCoordinate.y & 1 ) * fielddisthalfx, pos.y + localCoordinate.y * fielddisty),
00166 nullParam, nullParam, nullParam, nullParam );
00167
00168
00169 }
00170
00171
00172
00173 MapCoordinate BuildingType :: getFieldCoordinate ( const MapCoordinate& entryOnMap, const LocalCoordinate& localCoordinates ) const
00174 {
00175 int orgx = entryOnMap.x - entry.x - (entry.y & ~entryOnMap.y & 1 );
00176 int orgy = entryOnMap.y - entry.y;
00177
00178 int dx = orgy & 1;
00179
00180 int yy = orgy + localCoordinates.y;
00181 int xx = orgx + localCoordinates.x + (dx & ~yy);
00182 MapCoordinate mc ( xx, yy );
00183 return mc;
00184 }
00185
00186 BuildingType::LocalCoordinate BuildingType::getLocalCoordinate( const MapCoordinate& entryOnMap, const MapCoordinate& field ) const
00187 {
00188 int homex = entryOnMap.x - entry.x;
00189 int homey = entryOnMap.y - entry.y;
00190
00191 if ( (entry.y & 1) && !(entryOnMap.y & 1))
00192 homex -= 1;
00193
00194
00195 int ly = field.y - homey;
00196 int lx = field.x - homex;
00197 if ( (ly & 1) && (homey & 1 ))
00198 lx -= 1;
00199
00200 if ( lx >= 0 && lx < 4 && ly >= 0 && ly < 6 && fieldExists(LocalCoordinate(lx,ly)))
00201 return LocalCoordinate(lx,ly);
00202 else
00203 return LocalCoordinate();
00204 }
00205
00206
00207
00208 const int building_version = 12;
00209
00210
00211 void BuildingType :: read ( tnstream& stream )
00212 {
00213 int version = stream.readInt();
00214 if ( version <= building_version && version >= 1) {
00215
00216 bool picsAvail[ cwettertypennum ][ maxbuildingpicnum ][4][6];
00217
00218 for ( int v = 0; v < cwettertypennum; v++ )
00219 for ( int w = 0; w < maxbuildingpicnum; w++ )
00220 for ( int x = 0; x < 4; x++ )
00221 for ( int y = 0; y < 6 ; y++ ) {
00222 picsAvail[v][w][x][y] = stream.readInt( );
00223 if ( picsAvail[v][w][x][y] ) {
00224 field_Exists[x][y] = true;
00225 weatherBits.set(v);
00226 }
00227 }
00228
00229
00230 for ( int v = 0; v < cwettertypennum; v++ )
00231 for ( int w = 0; w < maxbuildingpicnum; w++ )
00232 for ( int x = 0; x < 4; x++ )
00233 for ( int y = 0; y < 6 ; y++ ) {
00234 int i = stream.readInt( );
00235 bi_picture[v][w][x][y] = i;
00236 if ( i > 0 ) {
00237 field_Exists[x][y] = true;
00238 weatherBits.set(v);
00239 }
00240 }
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253 entry.x = stream.readInt( );
00254 entry.y = stream.readInt( );
00255
00256 stream.readInt( );
00257 stream.readInt( );
00258 stream.readInt( );
00259 stream.readInt( );
00260
00261 id = stream.readInt( );
00262 bool __loadName = stream.readInt( );
00263 _armor = stream.readInt( );
00264 jamming = stream.readInt( );
00265 view = stream.readInt( );
00266 stream.readInt( );
00267 stream.readChar( );
00268 stream.readChar( );
00269 productionCost.material = stream.readInt( );
00270 productionCost.fuel = stream.readInt( );
00271 if ( version <= 9 ) {
00272 int special = stream.readInt( );
00273 convertOldFunctions( special, stream.getLocation() );
00274 }
00275
00276 technologylevel = stream.readChar( );
00277 researchid = stream.readChar( );
00278
00279 terrainaccess.read ( stream );
00280
00281 construction_steps = stream.readInt( );
00282 maxresearchpoints = stream.readInt( );
00283 asc_mode_tank.energy = stream.readInt( );
00284 asc_mode_tank.material = stream.readInt( );
00285 asc_mode_tank.fuel = stream.readInt( );
00286 maxplus.energy = stream.readInt( );
00287 maxplus.material = stream.readInt( );
00288 maxplus.fuel = stream.readInt( );
00289 efficiencyfuel = stream.readInt( );
00290 efficiencymaterial = stream.readInt( );
00291 stream.readInt( );
00292 stream.readInt( );
00293
00294 bi_mode_tank.energy = stream.readInt( );
00295 bi_mode_tank.material = stream.readInt( );
00296 bi_mode_tank.fuel = stream.readInt( );
00297
00298 if ( version >= 5 ) {
00299 defaultProduction.energy = stream.readInt( );
00300 defaultProduction.material = stream.readInt( );
00301 defaultProduction.fuel = stream.readInt( );
00302 }
00303
00304
00305 buildingheight = 1 << log2 ( stream.readInt() );
00306 stream.readInt( );
00307 externalloadheight = stream.readInt( );
00308
00309 if ( version >= 3)
00310 stream.readInt();
00311
00312
00313 if ( version >= 2 ) {
00314 if ( version <= 11 ) {
00315 for ( int x = 0; x < 4; x++ )
00316 for ( int y = 0; y < 6; y++ ) {
00317 int id = stream.readInt();
00318 if ( id > 0 )
00319 destructionObjects.insert( make_pair(LocalCoordinate(x,y), id));
00320 }
00321 }
00322 } else {
00323 for ( int w = 0; w < 9; w++ )
00324 stream.readInt( );
00325 destructionObjects.clear();
00326 }
00327
00328 if ( __loadName )
00329 name = stream.readString();
00330
00331 for ( int k = 0; k < maxbuildingpicnum ; k++)
00332 for ( int j = 0; j <= 5; j++)
00333 for ( int i = 0; i <= 3; i++)
00334 for ( int w = 0; w < cwettertypennum; w++ )
00335 if ( picsAvail[w][k][i][j] )
00336 if ( bi_picture[w][k][i][j] == -1 )
00337 w_picture[w][k][i][j].read(stream );
00338
00339
00340
00341 if ( version >= 4 )
00342 ContainerBaseType::read ( stream );
00343
00344 if ( version >= 6 )
00345 nominalresearchpoints = stream.readInt();
00346
00347 if ( version >= 7 ) {
00348 techDependency.read( stream );
00349 defaultMaxResearchpoints = stream.readInt();
00350 }
00351
00352 if ( version >= 8 )
00353 infotext = stream.readString();
00354
00355 if ( version >= 11 )
00356 buildingNotRemovable = stream.readInt();
00357
00358 if ( version >= 12 ) {
00359 int num = stream.readInt();
00360 for ( int i = 0; i < num; ++i ) {
00361 int x = stream.readInt();
00362 int y = stream.readInt();
00363 int id = stream.readInt();
00364 destructionObjects.insert( make_pair( LocalCoordinate(x,y),id));
00365 }
00366 }
00367
00368 } else
00369 throw tinvalidversion ( stream.getLocation(), building_version, version );
00370 }
00371
00372 void BuildingType :: write ( tnstream& stream ) const
00373 {
00374 stream.writeInt ( building_version );
00375
00376 for ( int v = 0; v < cwettertypennum; v++ )
00377 for ( int w = 0; w < maxbuildingpicnum; w++ )
00378 for ( int x = 0; x < 4; x++ )
00379 for ( int y = 0; y < 6 ; y++ ) {
00380 stream.writeInt ( w_picture[v][w][x][y].valid() );
00381 }
00382
00383 for ( int v = 0; v < cwettertypennum; v++ )
00384 for ( int w = 0; w < maxbuildingpicnum; w++ )
00385 for ( int x = 0; x < 4; x++ )
00386 for ( int y = 0; y < 6 ; y++ )
00387 stream.writeInt ( bi_picture[v][w][x][y] );
00388
00389
00390
00391
00392
00393
00394
00395
00396 stream.writeInt ( entry.x );
00397 stream.writeInt ( entry.y );
00398 stream.writeInt ( -1 );
00399 stream.writeInt ( -1 );
00400 stream.writeInt ( -1 );
00401 stream.writeInt ( -1 );
00402
00403 stream.writeInt ( id );
00404 stream.writeInt ( !name.empty() );
00405 stream.writeInt ( _armor );
00406 stream.writeInt ( jamming );
00407 stream.writeInt ( view );
00408 stream.writeInt ( 0 );
00409 stream.writeChar ( 0);
00410 stream.writeChar ( 0 );
00411 stream.writeInt ( productionCost.material );
00412 stream.writeInt ( productionCost.fuel );
00413 stream.writeChar ( technologylevel );
00414 stream.writeChar ( researchid );
00415
00416 terrainaccess.write ( stream );
00417
00418 stream.writeInt ( construction_steps );
00419 stream.writeInt ( maxresearchpoints );
00420 stream.writeInt ( asc_mode_tank.energy );
00421 stream.writeInt ( asc_mode_tank.material );
00422 stream.writeInt ( asc_mode_tank.fuel );
00423
00424 stream.writeInt ( maxplus.energy );
00425 stream.writeInt ( maxplus.material );
00426 stream.writeInt ( maxplus.fuel );
00427 stream.writeInt ( efficiencyfuel );
00428 stream.writeInt ( efficiencymaterial );
00429 stream.writeInt ( 1 );
00430 stream.writeInt ( 1 );
00431
00432 stream.writeInt ( bi_mode_tank.energy );
00433 stream.writeInt ( bi_mode_tank.material );
00434 stream.writeInt ( bi_mode_tank.fuel );
00435
00436 stream.writeInt ( defaultProduction.energy );
00437 stream.writeInt ( defaultProduction.material );
00438 stream.writeInt ( defaultProduction.fuel );
00439
00440 stream.writeInt ( buildingheight );
00441 stream.writeInt ( 0 );
00442 stream.writeInt ( externalloadheight );
00443
00444 stream.writeInt ( 0 );
00445
00446 if ( !name.empty() )
00447 stream.writeString ( name );
00448
00449 for (int k = 0; k < maxbuildingpicnum; k++)
00450 for (int j = 0; j <= 5; j++)
00451 for (int i = 0; i <= 3; i++)
00452 for ( int w = 0; w < cwettertypennum; w++ )
00453 if ( w_picture[w][k][i][j].valid() )
00454 if ( bi_picture[w][k][i][j] == -1 )
00455 w_picture[w][k][i][j].write(stream);
00456
00457 ContainerBaseType::write ( stream );
00458
00459 stream.writeInt( nominalresearchpoints );
00460
00461 techDependency.write ( stream );
00462 stream.writeInt( defaultMaxResearchpoints );
00463
00464 stream.writeString ( infotext );
00465 stream.writeInt( buildingNotRemovable );
00466
00467 stream.writeInt( destructionObjects.size());
00468 for ( DestructionObjects::const_iterator i = destructionObjects.begin(); i != destructionObjects.end(); ++i) {
00469 stream.writeInt( i->first.x);
00470 stream.writeInt( i->first.y);
00471 stream.writeInt( i->second);
00472 }
00473
00474 }
00475
00476
00477 ASCString BuildingType :: LocalCoordinate :: toString ( ) const
00478 {
00479 ASCString s;
00480 s += 'A'+x;
00481 s += '1'+y;
00482 return s;
00483 }
00484
00485 class InvalidString : public ASCexception {};
00486
00487 BuildingType :: LocalCoordinate :: LocalCoordinate ( const ASCString& s )
00488 {
00489 ASCString s2 = s;
00490 s2.toUpper();
00491 if ( s2.length() < 2 ) {
00492 x = -1;
00493 y = -1;
00494 throw InvalidString();
00495 } else {
00496 x = s2[0] - 'A';
00497 y = s2[1] - '1';
00498 if ( x < 0 || x > 5 || y < 0 || y > 7 )
00499 throw InvalidString();
00500 }
00501 }
00502
00503
00504 void BuildingType :: runTextIO ( PropertyContainer& pc )
00505 {
00506 try {
00507
00508 pc.addBreakpoint();
00509
00510 ContainerBaseType::runTextIO ( pc );
00511
00512 pc.addInteger ( "ConstructionStages", construction_steps );
00513
00514 for ( int i = 0; i < cwettertypennum; i++ )
00515 for ( int x = 0; x < 4; x++ )
00516 for ( int y = 0; y < 6; y++ )
00517 if ( w_picture[i][0][x][y].valid() )
00518 weatherBits.set(i);
00519
00520 pc.addTagArray( "Weather", weatherBits, cwettertypennum, weatherTags );
00521
00522
00523 ASCString fieldNames;
00524 for ( int a = 0; a < 4; a++ )
00525 for ( int b = 0; b < 6; b++ )
00526 if ( w_picture[0][0][a][b].valid() ) {
00527 fieldNames += LocalCoordinate( a, b).toString();
00528 fieldNames += " ";
00529 }
00530
00531 pc.addString( "Fields", fieldNames );
00532
00533 typedef vector<LocalCoordinate> Fields;
00534 Fields fields;
00535 StringTokenizer st ( fieldNames );
00536 ASCString t = st.getNextToken();
00537 while ( !t.empty() ) {
00538 LocalCoordinate lc ( t );
00539 fields.push_back ( lc );
00540 t = st.getNextToken();
00541 field_Exists[lc.x][lc.y] = true;
00542 }
00543
00544
00545
00546
00547 bool bi3pics = false;
00548
00549 for ( int i = 0; i < 4; i++ )
00550 for ( int j = 0; j < 6; j++ )
00551 if ( bi_picture[0][0][i][j] >= 0 )
00552 bi3pics = true;
00553
00554 pc.addBool ( "UseGFXpics", bi3pics );
00555
00556 if ( bi3pics ) {
00557 pc.openBracket ( "GFXpictures");
00558 for ( int w = 0; w < cwettertypennum; w++ )
00559 if ( weatherBits.test(w) ) {
00560 pc.openBracket (weatherTags[w] );
00561
00562 for ( int c = 0; c < construction_steps; c++ ) {
00563 pc.openBracket ( ASCString("Stage")+strrr(c+1) );
00564
00565 for ( Fields::iterator i = fields.begin(); i != fields.end(); i++ )
00566 pc.addInteger ( i->toString(), bi_picture[w][c][i->x][i->y] );
00567
00568
00569 pc.closeBracket();
00570 }
00571 pc.closeBracket();
00572
00573 }
00574 pc.closeBracket();
00575 } else {
00576 pc.openBracket ( "Pictures");
00577 if ( !pc.isReading() ) {
00578 for ( int w = 0; w < cwettertypennum; w++ )
00579 if ( weatherBits.test(w) ) {
00580 Surface s = Surface::createSurface( construction_steps*500, 250, 32, 0 );
00581 for ( int c = 0; c < construction_steps; c++ )
00582 for ( int x = 0; x < 4; x++ )
00583 for ( int y = 0; y < 6; y++ )
00584 if ( w_picture[w][c][x][y].valid() )
00585 megaBlitter<ColorTransform_None,
00586 ColorMerger_AlphaOverwrite,
00587 SourcePixelSelector_Plain,
00588 TargetPixelSelector_All>
00589 ( w_picture[w][c][x][y],
00590 s,
00591 SPoint( 500*c + x * fielddistx + (y&1)*fielddisthalfx, y * fielddisty),
00592 nullParam, nullParam, nullParam, nullParam );
00593 pc.addImage ( weatherTags[w], s, extractFileName_withoutSuffix ( filename )+weatherAbbrev[w]+".png", false );
00594 }
00595 } else {
00596 for ( int w = 0; w < cwettertypennum; w++ )
00597 if ( weatherBits.test(w) ) {
00598 ASCString fileName = extractFileName_withoutSuffix ( filename )+weatherAbbrev[w]+".png";
00599 Surface s;
00600 pc.addImage ( weatherTags[w], s, fileName, false );
00601
00602
00603
00604
00605 int depth = s.GetPixelFormat().BitsPerPixel();
00606 for ( int c = 0; c < construction_steps; c++ )
00607 for ( Fields::iterator i = fields.begin(); i != fields.end(); i++ ) {
00608 Surface& img = w_picture[w][c][i->x][i->y];
00609 img = Surface::createSurface(fieldsizex,fieldsizey,depth);
00610 int xx = 500*c + i->x * fielddistx + (i->y&1)*fielddisthalfx;
00611 int yy = i->y * fielddisty;
00612 if ( depth == 8 ) {
00613 img.Blit( s, SDLmm::SRect(SPoint(xx,yy),fieldsizex,fieldsizey), SPoint(0,0));
00614 applyFieldMask(img);
00615 } else {
00616 MegaBlitter<4,4,ColorTransform_None,ColorMerger_PlainOverwrite,SourcePixelSelector_Rectangle> blitter;
00617 blitter.setSrcRectangle( SDLmm::SRect(SPoint(xx,yy),fieldsizex,fieldsizey) );
00618 blitter.blit( s, img, SPoint(0,0) );
00619 applyFieldMask(img,0,0,false);
00620 }
00621 }
00622
00623 }
00624
00625 }
00626 pc.closeBracket();
00627 }
00628
00629 bool rubble = destructionObjects.size() > 0;
00630 pc.addBool ( "RubbleObjects", rubble );
00631 if ( rubble ) {
00632 if ( pc.isReading() )
00633 destructionObjects.clear();
00634
00635 pc.openBracket ( "Rubble");
00636 for ( Fields::iterator i = fields.begin(); i != fields.end(); i++ ) {
00637 vector<int> ids;
00638 typedef DestructionObjects::const_iterator J;
00639 pair<J,J> b = destructionObjects.equal_range(*i);
00640 for ( J j = b.first; j != b.second; ++j)
00641 ids.push_back(j->second);
00642 pc.addIntegerArray ( i->toString(), ids );
00643
00644 if ( pc.isReading() ) {
00645 for ( vector<int>::iterator j = ids.begin(); j != ids.end(); ++j )
00646 destructionObjects.insert(make_pair(*i, *j));
00647 }
00648 }
00649 pc.closeBracket();
00650 }
00651
00652
00653 ASCString entryString = entry.toString();
00654 pc.addString ( "Entry", entryString );
00655 if ( pc.isReading() ) {
00656 StringTokenizer st ( entryString );
00657 entry = LocalCoordinate ( st.getNextToken() );
00658 }
00659
00660 pc.addInteger( "Armor", _armor );
00661
00662 if ( pc.find( "Features" ) || !pc.isReading())
00663 pc.addTagArray ( "Features", features, functionNum, containerFunctionTags );
00664 else {
00665 int special = 0;
00666 pc.addTagInteger ( "Functions", special, cbuildingfunctionnum, buildingFunctionTags );
00667 convertOldFunctions( special, pc.getFileName() );
00668 }
00669 pc.addInteger ( "Techlevel", technologylevel );
00670
00671 pc.openBracket("TerrainAccess" );
00672 terrainaccess.runTextIO ( pc );
00673 pc.closeBracket();
00674
00675
00676 pc.openBracket ( "ConstructionCost" );
00677 productionCost.runTextIO ( pc );
00678 pc.closeBracket ();
00679
00680 pc.addTagInteger( "Height", buildingheight, choehenstufennum, heightTags );
00681
00682 pc.addTagInteger( "ExternalLoading", externalloadheight, choehenstufennum, heightTags );
00683
00684 pc.addBool ( "NotRemovable", buildingNotRemovable, false );
00685
00686 techDependency.runTextIO( pc, ASCString("b")+strrr(id) );
00687
00688 }
00689 catch ( InvalidString ) {
00690 pc.error ( "Could not parse building field coordinate");
00691 }
00692 }
00693
00694 void BuildingType::convertOldFunctions( int abilities, const ASCString& location )
00695 {
00696 features.reset();
00697 if ( abilities & 1 ) warning ( location + ": The HQ function for buildings is not supported any more");
00698 if ( abilities & 2 ) features.set( TrainingCenter );
00699 if ( abilities & (1 << 3) ) features.set( InternalVehicleProduction );
00700 if ( abilities & (1 << 4) ) features.set( AmmoProduction );
00701 if ( abilities & (1 << 8) ) features.set( InternalUnitRepair );
00702 if ( abilities & (1 << 9) ) features.set( RecycleUnits );
00703 if ( abilities & (1 << 10) ) features.set( Research );
00704 if ( abilities & (1 << 11) ) features.set( Sonar );
00705 if ( abilities & (1 << 12) ) features.set( WindPowerPlant );
00706 if ( abilities & (1 << 13) ) features.set( SolarPowerPlant );
00707 if ( abilities & (1 << 14) ) features.set( MatterConverter );
00708 if ( abilities & (1 << 15) ) features.set( MiningStation );
00709 if ( abilities & (1 << 16) ) {
00710 features.set( ExternalMaterialTransfer );
00711 features.set( ExternalFuelTransfer );
00712 features.set( ExternalAmmoTransfer );
00713 }
00714 if ( abilities & (1 << 17) ) features.set( ProduceNonLeavableUnits );
00715 if ( abilities & (1 << 18) ) features.set( ResourceSink );
00716 if ( abilities & (1 << 19) ) {
00717 features.set( ExternalMaterialTransfer );
00718 features.set( ExternalFuelTransfer );
00719 }
00720 if ( abilities & (1 << 20) ) features.set( ExternalAmmoTransfer );
00721 if ( abilities & (1 << 21) ) features.set( NoObjectChaining );
00722 if ( abilities & (1 << 22) ) features.set( SelfDestructOnConquer );
00723 if ( abilities & (1 << 23) ) features.set( SatelliteView );
00724 }
00725