00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <algorithm>
00023 #include <ctime>
00024 #include <cmath>
00025
00026 #include "global.h"
00027 #include "misc.h"
00028 #include "typen.h"
00029 #include "vehicletype.h"
00030 #include "buildingtype.h"
00031 #include "gamemap.h"
00032 #include "dialog.h"
00033 #include "itemrepository.h"
00034 #include "strtmesg.h"
00035
00036 #include "overviewmapimage.h"
00037 #include "gameeventsystem.h"
00038 #include "spfst.h"
00039 #include "campaignactionrecorder.h"
00040
00041 #include "packagemanager.h"
00042 #include "tasks/abstracttaskcontainer.h"
00043
00044
00045 RandomGenerator::RandomGenerator(int seedValue){
00046
00047 }
00048
00049 RandomGenerator::~RandomGenerator(){
00050
00051
00052 }
00053
00054 unsigned int RandomGenerator::getPercentage(){
00055 return getRandomValue(100);
00056 }
00057
00058 unsigned int RandomGenerator::getRandomValue(int limit) {
00059 return getRandomValue(0, limit);
00060 }
00061
00062 unsigned int RandomGenerator::getRandomValue (int lowerLimit, int upperLimit){
00063 if(upperLimit == 0) {
00064 return 1;
00065 }
00066 int random_integer = rand();
00067 random_integer = random_integer % upperLimit;
00068 return (lowerLimit + random_integer);
00069 }
00070
00071
00072
00073
00074 OverviewMapHolder :: OverviewMapHolder( GameMap& gamemap ) : map(gamemap), initialized(false), secondMapReady(false), completed(false), connected(false), x(0),y(0)
00075 {
00076 }
00077
00078 void OverviewMapHolder :: connect()
00079 {
00080 if ( !connected ) {
00081 idleEvent.connect ( SigC::slot( *this, &OverviewMapHolder::idleHandler ));
00082 connected = true;
00083 }
00084 }
00085
00086
00087 SigC::Signal0<void> OverviewMapHolder::generationComplete;
00088
00089 bool OverviewMapHolder :: idleHandler( )
00090 {
00091 int t = ticker;
00092 while ( !completed && (t + 5 > ticker ))
00093 drawNextField( true );
00094 return true;
00095 }
00096
00097
00098 bool OverviewMapHolder::updateField( const MapCoordinate& pos )
00099 {
00100 SPoint imgpos = OverviewMapImage::map2surface( pos );
00101
00102 MapField* fld = map.getField( pos );
00103 VisibilityStates visi = fieldVisibility( fld, map.getPlayerView() );
00104 if ( visi == visible_not ) {
00105 OverviewMapImage::fill ( overviewMapImage, imgpos, 0xff545454 );
00106 } else {
00107 if ( fld->building && fieldvisiblenow( fld, map.getPlayerView()) )
00108 OverviewMapImage::fill ( overviewMapImage, imgpos, map.player[fld->building->getOwner()].getColor() );
00109 else {
00110
00111 int w = fld->getWeather();
00112 fld->typ->getQuickView()->blit( overviewMapImage, imgpos );
00113 for ( MapField::ObjectContainer::iterator i = fld->objects.begin(); i != fld->objects.end(); ++i )
00114 if ( visi > visible_ago || i->typ->visibleago )
00115 i->getOverviewMapImage( w )->blit( overviewMapImage, imgpos );
00116
00117 if ( fld->vehicle && fieldvisiblenow( fld, map.getPlayerView()) )
00118 OverviewMapImage::fillCenter ( overviewMapImage, imgpos, map.player[fld->vehicle->getOwner()].getColor() );
00119
00120 if ( visi == visible_ago )
00121 OverviewMapImage::lighten( overviewMapImage, imgpos, 0.7 );
00122
00123 }
00124
00125 }
00126 return true;
00127 }
00128
00129 void OverviewMapHolder::drawNextField( bool signalOnCompletion )
00130 {
00131 if ( !init() )
00132 return;
00133
00134 if ( x == map.xsize ) {
00135 x = 0;
00136 ++y;
00137 }
00138 if ( y < map.ysize ) {
00139 if ( !updateField( MapCoordinate(x,y)))
00140 return;
00141
00142 ++x;
00143 }
00144 if ( y == map.ysize ) {
00145 completed = true;
00146 if ( signalOnCompletion )
00147 generationComplete();
00148
00149 completedMapImage = overviewMapImage.Duplicate();
00150 secondMapReady = true;
00151 }
00152 }
00153
00154 Surface OverviewMapHolder::createNewSurface()
00155 {
00156 Surface s;
00157 if ( map.xsize > 0 && map.ysize > 0 ) {
00158 s = Surface::createSurface( (map.xsize+1) * 6, 4 + map.ysize * 2 , 32, 0 );
00159 }
00160 return s;
00161 }
00162
00163 bool OverviewMapHolder::init()
00164 {
00165 if ( map.ysize <= 0 || map.xsize <= 0 )
00166 return false;
00167
00168 if ( !initialized ) {
00169 overviewMapImage = createNewSurface();
00170 initialized = true;
00171 }
00172 return initialized;
00173 }
00174
00175 void OverviewMapHolder::resetSize()
00176 {
00177 initialized = false;
00178 }
00179
00180
00181 const Surface& OverviewMapHolder::getOverviewMap( bool complete )
00182 {
00183 bool initialized = init();
00184 assert( initialized );
00185 if ( complete )
00186 while ( !completed )
00187 drawNextField( false );
00188
00189 if( secondMapReady )
00190 return completedMapImage;
00191 else
00192 return overviewMapImage;
00193 }
00194
00195 void OverviewMapHolder::startUpdate()
00196 {
00197 completed = false;
00198 x = 0;
00199 y = 0;
00200 }
00201
00202 void OverviewMapHolder::clear(bool allImages )
00203 {
00204 if ( !initialized )
00205 return;
00206
00207 overviewMapImage.Fill( Surface::transparent );
00208 if ( allImages ) {
00209 if ( completedMapImage.valid() )
00210 completedMapImage.Fill( Surface::transparent );
00211 secondMapReady = false;
00212 }
00213
00214 startUpdate();
00215 }
00216
00217 void OverviewMapHolder::clearmap( GameMap* actmap )
00218 {
00219 if ( actmap )
00220 actmap->overviewMapHolder.clear();
00221 }
00222
00223
00224 GameMap :: GameMap ( void )
00225 : actions(this), actionRecorder(NULL), overviewMapHolder( *this ), network(NULL), packageData(NULL)
00226 {
00227 serverMapID = 0;
00228 randomSeed = rand();
00229 dialogsHooked = false;
00230
00231 eventID = 0;
00232
00233 state = Normal;
00234
00235 xsize = 0;
00236 ysize = 0;
00237 field = NULL;
00238
00239 actplayer = -1;
00240 time.abstime = 0;
00241
00242 _resourcemode = 0;
00243
00244 for ( int i = 0; i < 9; ++i ) {
00245 player[i].setParentMap ( this, i );
00246 if ( i == 0 )
00247 player[i].stat = Player::human;
00248 else
00249 player[i].stat = Player::computer;
00250
00251 player[i].research.chainToMap ( this, i );
00252 }
00253
00254 levelfinished = 0;
00255
00256 messageid = 0;
00257
00258 continueplaying = false;
00259 replayinfo = NULL;
00260 playerView = 0;
00261 lastjournalchange.abstime = 0;
00262 graphicset = 0;
00263 gameparameter_num = 0;
00264 game_parameter = NULL;
00265 mineralResourcesDisplayed = 0;
00266
00267
00268 #ifdef WEATHERGENERATOR
00269 weatherSystem = new WeatherSystem(this, 1, 0.03);
00270 #endif
00271 setgameparameter( cgp_objectsDestroyedByTerrain, 1 );
00272
00273
00274 nativeMessageLanguage = "en_US";
00275
00276 sigMapCreation( *this );
00277 }
00278
00279 GameMap::Campaign::Campaign()
00280 {
00281 avail = false;
00282 id = 0;
00283 directaccess = true;
00284 }
00285
00286 void GameMap :: guiHooked()
00287 {
00288 overviewMapHolder.connect();
00289 dialogsHooked = true;
00290 }
00291
00292 const int tmapversion = 34;
00293
00294 void GameMap :: read ( tnstream& stream )
00295 {
00296 int version;
00297
00298 xsize = stream.readWord();
00299 ysize = stream.readWord();
00300
00301 if ( xsize == 0xfffe && ysize == 0xfffc ) {
00302 version = stream.readInt();
00303 if ( version > tmapversion )
00304 throw tinvalidversion ( "GameMap", tmapversion, version );
00305
00306 xsize = stream.readInt();
00307 ysize = stream.readInt();
00308 } else
00309 version = 1;
00310
00311 stream.readWord();
00312 stream.readWord();
00313 stream.readInt();
00314 field = NULL;
00315
00316 if ( version <= 13 ) {
00317 char buf[11];
00318 stream.readdata ( buf, 11 );
00319 buf[10] = 0;
00320 codeWord = buf;
00321 } else {
00322 codeWord = stream.readString();
00323 }
00324
00325 if ( version < 2 )
00326 ___loadtitle = stream.readInt();
00327 else
00328 ___loadtitle = true;
00329
00330
00331 bool loadCampaign = stream.readInt();
00332 actplayer = stream.readChar();
00333 time.abstime = stream.readInt();
00334 if(version < 9 || version >= 17){
00335 stream.readChar();
00336 weather.windSpeed = stream.readChar();
00337 weather.windDirection = stream.readChar();
00338 }
00339
00340 if ( version >= 11 )
00341 if ( stream.readInt() != 0x12345678 )
00342 throw ASCmsgException("marker not matched when loading GameMap");
00343
00344 if ( version >= 32 ) {
00345 int p = stream.readInt();
00346 if ( p ) {
00347 packageData = new PackageData();
00348 packageData->read( stream );
00349
00350 PackageManager::checkGame( this );
00351
00352 } else
00353 packageData = NULL;
00354 } else
00355 packageData = NULL;
00356
00357
00358 for ( int j = 0; j < 4; j++ )
00359 stream.readChar();
00360 for ( int i = 0; i< 12; i++ )
00361 stream.readChar();
00362
00363 _resourcemode = stream.readInt();
00364
00365 int alliances[8][8];
00366 if ( version <= 10 )
00367 for ( int i = 0; i < 8; i++ )
00368 for ( int j = 0; j < 8; j++ )
00369 alliances[j][i] = stream.readChar();
00370
00371 int dummy_playername[9];
00372 for ( int i = 0; i< 9; i++ ) {
00373 player[i].existanceAtBeginOfTurn = stream.readChar();
00374 stream.readInt();
00375 stream.readInt();
00376 if ( version <= 5 )
00377 player[i].research.read_struct ( stream );
00378 else
00379 player[i].research.read ( stream );
00380
00381
00382 stream.readInt() ;
00383 player[i].ai = NULL;
00384 player[i].stat = Player::PlayerStatus ( stream.readChar() );
00385 stream.readChar();
00386 dummy_playername[i] = stream.readInt();
00387 player[i].passwordcrc.read ( stream );
00388 player[i].__dissectionsToLoad = stream.readInt();
00389 player[i].__loadunreadmessage = stream.readInt();
00390 player[i].__loadoldmessage = stream.readInt();
00391 player[i].__loadsentmessage = stream.readInt();
00392 if ( version >= 3 )
00393 player[i].ASCversion = stream.readInt();
00394 else
00395 player[i].ASCversion = 0;
00396
00397 if ( version >= 9 )
00398 player[i].cursorPos.read( stream );
00399
00400 if ( version >= 11 )
00401 player[i].diplomacy.read( stream );
00402 else {
00403 if ( i < 8 )
00404 for ( int j = 0; j< 8; ++j ) {
00405 if ( alliances[i][j] == 0 )
00406 player[i].diplomacy.setState( j, PEACE );
00407 else
00408 player[i].diplomacy.setState( j, WAR );
00409 }
00410 }
00411
00412 if ( version >= 12 )
00413 player[i].email = stream.readString();
00414
00415 if ( version >= 22 )
00416 player[i].read( stream );
00417 }
00418
00419 if ( version >= 11 )
00420 if ( stream.readInt() != 0x12345678 )
00421 throw ASCmsgException("marker not matched when loading GameMap");
00422
00423
00424
00425
00426 if ( version <= 4 ) {
00428 loadOldEvents = stream.readInt();
00429 stream.readInt();
00430 stream.readInt();
00431 }
00432
00433
00434 idManager.readData(stream );
00435
00436 levelfinished = stream.readChar();
00437
00438 bool alliance_names_not_used_any_more[8];
00439 if ( version <= 9 ) {
00440 ___loadLegacyNetwork = stream.readInt();
00441 for ( int i = 0; i < 8; i++ )
00442 alliance_names_not_used_any_more[i] = stream.readInt();
00443 } else {
00444 ___loadLegacyNetwork = false;
00445 for ( int i = 0; i < 8; i++ )
00446 alliance_names_not_used_any_more[i] = 0;
00447 }
00448
00449 if ( version <= 12 ) {
00450 for ( int i = 0; i< 8; i++ ) {
00451 stream.readWord();
00452 stream.readWord();
00453 stream.readWord();
00454 stream.readWord();
00455 }
00456 }
00457
00458 if ( version <= 9 )
00459 stream.readInt();
00460
00461 __loadunsentmessage = stream.readInt();
00462 __loadmessages = stream.readInt();
00463
00464 messageid = stream.readInt();
00465
00466 if( version < 2 ) {
00467 ___loadJournal = stream.readInt();
00468 ___loadNewJournal = stream.readInt();
00469 } else {
00470 ___loadJournal = true;
00471 ___loadNewJournal = true;
00472 }
00473
00474 int exist_humanplayername[9];
00475 for ( int i = 0; i < 8; i++ )
00476 exist_humanplayername[i] = stream.readInt();
00477 exist_humanplayername[8] = 0;
00478
00479
00480 int exist_computerplayername[9];
00481 for ( int i = 0; i < 8; i++ )
00482 exist_computerplayername[i] = stream.readInt();
00483 exist_computerplayername[8] = 0;
00484
00485 supervisorpasswordcrc.read ( stream );
00486
00487 if ( version <= 10 )
00488 for ( int i = 0; i < 8; i++ )
00489 stream.readChar();
00490
00491 stream.readInt();
00492 bool load_shareview = false;
00493 if ( version <= 10 )
00494 load_shareview = stream.readInt();
00495
00496 continueplaying = stream.readInt();
00497 __loadreplayinfo = stream.readInt();
00498 playerView = stream.readInt();
00499 lastjournalchange.abstime = stream.readInt();
00500
00501 for ( int i = 0; i< 8; i++ )
00502 bi_resource[i].read ( stream );
00503
00504 int preferredfilenames = stream.readInt();
00505
00506 bool __loadEllipse = stream.readInt();
00507 graphicset = stream.readInt();
00508 gameparameter_num = stream.readInt();
00509
00510 stream.readInt();
00511 mineralResourcesDisplayed = stream.readInt();
00512 for ( int i = 0; i< 9; i++ )
00513 player[i].queuedEvents = stream.readInt();
00514
00515 for ( int i = 0; i < 19; i++ )
00516 stream.readInt();
00517
00518 int _oldgameparameter[8];
00519 for ( int i = 0; i < 8; i++ )
00520 _oldgameparameter[i] = stream.readInt();
00521
00522 if ( version >= 11 )
00523 if ( stream.readInt() != 0x12345678 )
00524 throw ASCmsgException("marker not matched when loading GameMap");
00525
00527
00529
00530
00531
00532 if ( ___loadtitle )
00533 maptitle = stream.readString();
00534
00535 if ( loadCampaign ) {
00536 if ( version <= 14 ) {
00537 campaign.id = stream.readWord();
00538 stream.readWord();
00539 stream.readChar();
00540 campaign.directaccess = stream.readChar();
00541 campaign.avail = true;
00542 for ( int d = 0; d < 21; d++ )
00543 stream.readChar();
00544 } else {
00545 campaign.id = stream.readInt();
00546 campaign.directaccess = stream.readChar();
00547 if ( version > 15 )
00548 campaign.avail = stream.readInt();
00549 }
00550 }
00551
00552 for ( int w=0; w<9 ; w++ ) {
00553 if (dummy_playername[w] )
00554 stream.readString();
00555
00556 player[w].ai = NULL;
00557
00558
00559 if ( exist_humanplayername[w] )
00560 player[w].setName( stream.readString() );
00561
00562 if ( exist_computerplayername[w] )
00563 stream.readString();
00564
00565 }
00566
00567 if ( stream.readInt() )
00568 tribute.read ( stream );
00569
00570 for ( int aa = 0; aa < 8; aa++ )
00571 if ( alliance_names_not_used_any_more[aa] ) {
00572 char* tempname = NULL;
00573 stream.readpchar ( &tempname );
00574 delete[] tempname;
00575 }
00576
00577 stream.readInt();
00578
00579 if ( load_shareview && version <= 10 ) {
00580
00581 for ( int i = 0; i < 8; i++ )
00582 for ( int j =0; j < 8; j++ ) {
00583 int sv = stream.readChar();
00584 if ( sv )
00585 player[i].diplomacy.setState( j, PEACE_SV );
00586 }
00587
00588 stream.readInt();
00589 }
00590
00591 if ( preferredfilenames ) {
00592 int p;
00593 int mapname[8];
00594 int mapdescription_not_used_any_more[8];
00595 int savegame[8];
00596 int savegamedescription_not_used_any_more[8];
00597 for ( p = 0; p < 8; p++ )
00598 mapname[p] = stream.readInt();
00599 for ( p = 0; p < 8; p++ )
00600 mapdescription_not_used_any_more[p] = stream.readInt();
00601 for ( p = 0; p < 8; p++ )
00602 savegame[p] = stream.readInt();
00603 for ( p = 0; p < 8; p++ )
00604 savegamedescription_not_used_any_more[p] = stream.readInt();
00605
00606 for ( int i = 0; i < 8; i++ ) {
00607 if ( mapname[i] )
00608 preferredFileNames.mapname[i] = stream.readString ();
00609
00610 if ( mapdescription_not_used_any_more[i] )
00611 stream.readString();
00612
00613 if ( savegame[i] )
00614 preferredFileNames.savegame[i] = stream.readString ();
00615
00616 if ( savegamedescription_not_used_any_more[i] )
00617 stream.readString();
00618 }
00619 }
00620
00621 if ( __loadEllipse ) {
00622 for ( int i = 0; i < 5; ++i )
00623 stream.readInt();
00624 stream.readFloat();
00625 stream.readInt();
00626 }
00627
00628 int orggpnum = gameparameter_num;
00629 gameparameter_num = 0;
00630 for ( int gp = 0; gp < 8; gp ++ )
00631 setgameparameter ( GameParameter(gp), _oldgameparameter[gp] );
00632
00633 for ( int ii = 0 ; ii < orggpnum; ii++ ) {
00634 int gpar = stream.readInt();
00635 setgameparameter ( GameParameter(ii), gpar );
00636 }
00637
00638 if ( version >= 2 ) {
00639 archivalInformation.author = stream.readString();
00640 archivalInformation.description = stream.readString();
00641 archivalInformation.tags = stream.readString();
00642 archivalInformation.requirements = stream.readString();
00643 archivalInformation.modifytime = stream.readInt();
00644 }
00645
00646 if ( version >= 4 ) {
00647 int num = stream.readInt();
00648 for ( int ii = 0; ii < num; ++ii )
00649 unitProduction.idsAllowed.push_back ( stream.readInt() );
00650
00651 for ( int ii = 0; ii < 9; ii++ ) {
00652 int num = stream.readInt( );
00653 for ( int i = 0; i < num; i++ ) {
00654 Player::PlayTime pt;
00655 pt.turn = stream.readInt();
00656 pt.date = stream.readInt();
00657 player[ii].playTime.push_back ( pt );
00658 }
00659 }
00660
00661 }
00662
00663 if ( version >= 5 ) {
00664 eventID = stream.readInt();
00665
00666 int num = stream.readInt();
00667 for ( int i = 0; i< num; ++i ) {
00668 Event* ev = new Event ( *this );
00669 ev->read ( stream );
00670 events.push_back ( ev );
00671 }
00672 }
00673
00674 if ( version >= 8 )
00675 randomSeed = stream.readInt();
00676
00677 if ( version >= 12 ) {
00678 bool nw = stream.readInt();
00679 if ( nw )
00680 network = GameTransferMechanism::read( stream );
00681 }
00682
00683 if ( version >= 25 )
00684 actions.read( stream );
00685
00686 if ( version >= 30 ) {
00687 nativeMessageLanguage = stream.readString();
00688 int recorder = stream.readInt();
00689 if ( recorder == 1 ) {
00690 actionRecorder = new CampaignActionLogger( this );
00691 actionRecorder->readData( stream );
00692 } else
00693 actionRecorder = NULL;
00694 }
00695
00696 if( version >= 31 )
00697 properties.read( stream );
00698
00699 if ( version >= 33 )
00700 tasks->read( stream );
00701
00702 if ( version >= 34 )
00703 serverMapID = stream.readInt();
00704
00705 }
00706
00707
00708 void GameMap :: write ( tnstream& stream )
00709 {
00710 int i;
00711
00712 stream.writeWord( 0xfffe );
00713 stream.writeWord( 0xfffc );
00714
00715 stream.writeInt ( tmapversion );
00716 stream.writeInt( xsize );
00717 stream.writeInt( ysize );
00718
00719 stream.writeWord( 0 );
00720 stream.writeWord( 0 );
00721 stream.writeInt (1);
00722 stream.writeString ( codeWord );
00723
00724
00725 stream.writeInt( campaign.avail );
00726 stream.writeChar( actplayer );
00727 stream.writeInt( time.abstime );
00728
00729 stream.writeChar(0);
00730 stream.writeChar( weather.windSpeed );
00731 stream.writeChar( weather.windDirection );
00732
00733 stream.writeInt( 0x12345678 );
00734
00735 stream.writeInt( packageData != NULL );
00736 if ( packageData )
00737 packageData->write( stream );
00738
00739
00740 for ( i= 0; i < 4; i++ )
00741 stream.writeChar( 0 );
00742
00743 for ( i = 0; i< 12; i++ )
00744 stream.writeChar( 0 );
00745
00746 stream.writeInt( _resourcemode );
00747
00748 for ( i = 0; i< 9; i++ ) {
00749 stream.writeChar( player[i].existanceAtBeginOfTurn );
00750 stream.writeInt( 1 );
00751 stream.writeInt( 1 );
00752 player[i].research.write ( stream );
00753 stream.writeInt( player[i].ai != NULL );
00754 stream.writeChar( player[i].stat );
00755 stream.writeChar( 0 );
00756 stream.writeInt( 0 );
00757 player[i].passwordcrc.write ( stream );
00758 stream.writeInt( !player[i].dissections.empty() );
00759 stream.writeInt( 1 );
00760 stream.writeInt( 1 );
00761 stream.writeInt( 1 );
00762 stream.writeInt ( player[i].ASCversion );
00763 player[i].cursorPos.write( stream );
00764 player[i].diplomacy.write( stream );
00765 stream.writeString ( player[i].email );
00766 player[i].write(stream);
00767 }
00768
00769 stream.writeInt( 0x12345678 );
00770
00771 idManager.writeData(stream);
00772
00773 stream.writeChar( levelfinished );
00774
00775 stream.writeInt( 1 );
00776 stream.writeInt( !messages.empty() );
00777
00778 stream.writeInt( messageid );
00779
00780 for ( i = 0; i < 8; i++ )
00781 stream.writeInt( 1 );
00782
00783 for ( i = 0; i < 8; i++ )
00784 stream.writeInt( 0 );
00785
00786 supervisorpasswordcrc.write ( stream );
00787
00788 stream.writeInt( 0 );
00789
00790 stream.writeInt( continueplaying );
00791 stream.writeInt( replayinfo != NULL );
00792 stream.writeInt( playerView );
00793 stream.writeInt( lastjournalchange.abstime );
00794
00795 for ( i = 0; i< 8; i++ )
00796 bi_resource[i].write ( stream );
00797
00798 stream.writeInt( 1 );
00799 stream.writeInt( 0 );
00800 stream.writeInt( graphicset );
00801 stream.writeInt( gameparameter_num );
00802
00803 stream.writeInt( game_parameter != NULL );
00804 stream.writeInt( mineralResourcesDisplayed );
00805 for ( i = 0; i< 9; i++ )
00806 stream.writeInt( player[i].queuedEvents );
00807
00808 for ( i = 0; i < 19; i++ )
00809 stream.writeInt( 0 );
00810
00811 for ( i = 0; i < 8; i++ )
00812 stream.writeInt( getgameparameter(GameParameter(i)) );
00813
00814
00815 stream.writeInt( 0x12345678 );
00816
00817
00819
00821
00822
00823
00824 stream.writeString( maptitle );
00825
00826 if ( campaign.avail ) {
00827 stream.writeInt( campaign.id );
00828 stream.writeChar( campaign.directaccess );
00829 stream.writeInt( campaign.avail );
00830 }
00831
00832 for (int w=0; w<8 ; w++ )
00833 stream.writeString ( player[w].getName() );
00834
00835 if ( !tribute.empty() ) {
00836 stream.writeInt ( -1 );
00837 tribute.write ( stream );
00838 } else
00839 stream.writeInt ( 0 );
00840
00841 stream.writeInt ( 0 );
00842
00843 int p;
00844 for ( p = 0; p < 8; p++ )
00845 stream.writeInt( 1 );
00846
00847 for ( p = 0; p < 8; p++ )
00848 stream.writeInt( 0 );
00849
00850 for ( p = 0; p < 8; p++ )
00851 stream.writeInt( 1 );
00852
00853 for ( p = 0; p < 8; p++ )
00854 stream.writeInt( 0 );
00855
00856 for ( int k = 0; k < 8; k++ ) {
00857 stream.writeString ( preferredFileNames.mapname[k] );
00858 stream.writeString ( preferredFileNames.savegame[k] );
00859 }
00860
00861
00862 for ( int ii = 0 ; ii < gameparameter_num; ii++ )
00863 stream.writeInt ( game_parameter[ii] );
00864
00865
00866 stream.writeString ( archivalInformation.author );
00867 stream.writeString ( archivalInformation.description );
00868 stream.writeString ( archivalInformation.tags );
00869 stream.writeString ( archivalInformation.requirements );
00870 stream.writeInt ( (unsigned int) (::time ( &archivalInformation.modifytime )));
00871
00872
00873 stream.writeInt( unitProduction.idsAllowed.size() );
00874 for ( int ii = 0; ii < unitProduction.idsAllowed.size(); ++ii )
00875 stream.writeInt ( unitProduction.idsAllowed[ii] );
00876
00877
00878 for ( int ii = 0; ii < 9; ii++ ) {
00879 stream.writeInt( player[ii].playTime.size() );
00880 for ( Player::PlayTimeContainer::iterator i = player[ii].playTime.begin(); i != player[ii].playTime.end(); ++i ) {
00881 stream.writeInt( i->turn );
00882 stream.writeInt( (unsigned int) i->date );
00883 }
00884 }
00885
00886 stream.writeInt( eventID );
00887
00888 stream.writeInt ( events.size());
00889 for ( Events::iterator i = events.begin(); i != events.end(); ++i )
00890 (*i)->write( stream );
00891
00892 stream.writeInt( randomSeed );
00893
00894 if ( network ) {
00895 stream.writeInt( 1 );
00896 network->write( stream );
00897 } else
00898 stream.writeInt( 0 );
00899
00900 actions.write( stream );
00901
00902 stream.writeString(nativeMessageLanguage);
00903
00904 if ( actionRecorder != NULL ) {
00905 stream.writeInt( 1 );
00906 actionRecorder->writeData( stream );
00907 } else
00908 stream.writeInt( 0 );
00909
00910 properties.write( stream );
00911
00912 tasks->write( stream );
00913
00914 stream.writeInt( serverMapID );
00915 }
00916
00917
00918 MapCoordinate GameMap::findFirstContainer() const
00919 {
00920 for ( int y = 0; y < ysize; ++y )
00921 for ( int x = 0; x < xsize; ++x )
00922 if ( getField(x,y)->getContainer() )
00923 if ( getField(x,y)->getContainer()->getOwner() == actplayer )
00924 return MapCoordinate(x,y);
00925
00926 return MapCoordinate(0,0);
00927 }
00928
00929
00930 MapCoordinate& GameMap::getCursor()
00931 {
00932 #ifdef sgmain
00933 if ( actplayer >= 0 ) {
00934 if ( !player[actplayer].cursorPos.valid() || player[actplayer].cursorPos.x >= xsize || player[actplayer].cursorPos.y >= ysize)
00935 player[actplayer].cursorPos = findFirstContainer();
00936
00937 return player[actplayer].cursorPos;
00938 } else
00939 return player[0].cursorPos;
00940 #else
00941 return player[8].cursorPos;
00942 #endif
00943 }
00944
00945 MapCoordinate GameMap::getCursor() const
00946 {
00947 #ifdef sgmain
00948 if ( actplayer >= 0 ) {
00949 if ( !player[actplayer].cursorPos.valid() || player[actplayer].cursorPos.x >= xsize || player[actplayer].cursorPos.y >= ysize)
00950 return findFirstContainer();
00951 else
00952 return player[actplayer].cursorPos;
00953 } else
00954 return player[0].cursorPos;
00955 #else
00956 return player[8].cursorPos;
00957 #endif
00958 }
00959
00960
00961 void GameMap :: cleartemps( int b, int value )
00962 {
00963 if ( xsize <= 0 || ysize <= 0)
00964 return;
00965
00966 int l = 0;
00967 for ( int x = 0; x < xsize ; x++)
00968 for ( int y = 0; y < ysize ; y++) {
00969
00970 if (b & 1 )
00971 field[l].a.temp = value;
00972 if (b & 2 )
00973 field[l].a.temp2 = value;
00974 if (b & 4 )
00975 field[l].temp3 = value;
00976 if (b & 8 )
00977 field[l].temp4 = value;
00978
00979 l++;
00980 }
00981 }
00982
00983 void GameMap :: allocateFields ( int x, int y, TerrainType::Weather* terrain )
00984 {
00985 field = new MapField[x*y];
00986 for ( int i = 0; i < x*y; i++ ) {
00987 if ( terrain ) {
00988 field[i].typ = terrain;
00989 field[i].setparams();
00990 }
00991 field[i].setMap ( this );
00992 }
00993 xsize = x;
00994 ysize = y;
00995 overviewMapHolder.connect();
00996 }
00997
00998
00999 void GameMap :: calculateAllObjects ( void )
01000 {
01001 calculateallobjects( this );
01002 }
01003
01004 MapField* GameMap :: getField(int x, int y)
01005 {
01006 if ((x < 0) || (y < 0) || (x >= xsize) || (y >= ysize))
01007 return NULL;
01008 else
01009 return ( &field[y * xsize + x] );
01010 }
01011
01012 const MapField* GameMap :: getField(int x, int y) const
01013 {
01014 if ((x < 0) || (y < 0) || (x >= xsize) || (y >= ysize))
01015 return NULL;
01016 else
01017 return ( &field[y * xsize + x] );
01018 }
01019
01020
01021 MapField* GameMap :: getField(const MapCoordinate& pos )
01022 {
01023 return getField ( pos.x, pos.y );
01024 }
01025
01026 int GameMap :: getPlayerView() const
01027 {
01028 #ifdef karteneditor
01029 return -1;
01030 #else
01031 return playerView;
01032 #endif
01033 }
01034
01035
01036 void GameMap :: setPlayerView( int player )
01037 {
01038 playerView = player;
01039 }
01040
01041
01042
01043 bool GameMap :: isResourceGlobal ( int resource )
01044 {
01045 if ( _resourcemode == 1 ) {
01046 if ( resource == 1 )
01047 return false;
01048 else
01049 if ( resource == 2 )
01050 return getgameparameter(cgp_globalfuel);
01051 else
01052 return true;
01053 } else {
01054
01055
01056
01057
01058
01059
01060
01061
01062 return false;
01063 }
01064 }
01065
01066 int GameMap :: getgameparameter ( GameParameter num ) const
01067 {
01068 if ( game_parameter && num < gameparameter_num ) {
01069 return game_parameter[num];
01070 } else
01071 if ( num < gameparameternum )
01072 return gameParameterSettings[num].defaultValue;
01073 else
01074 return 0;
01075 }
01076
01077 void GameMap :: setgameparameter ( GameParameter num, int value )
01078 {
01079 if ( game_parameter ) {
01080 if ( num < gameparameter_num )
01081 game_parameter[num] = value;
01082 else {
01083 int* oldparam = game_parameter;
01084 game_parameter = new int[num+1];
01085 for ( int i = 0; i < gameparameter_num; i++ )
01086 game_parameter[i] = oldparam[i];
01087 for ( int j = gameparameter_num; j < num; j++ )
01088 if ( j < gameparameternum )
01089 game_parameter[j] = gameParameterSettings[j].defaultValue;
01090 else
01091 game_parameter[j] = 0;
01092 game_parameter[num] = value;
01093 gameparameter_num = num + 1;
01094 delete[] oldparam;
01095 }
01096 } else {
01097 game_parameter = new int[num+1];
01098 for ( int j = 0; j < num; j++ )
01099 if ( j < gameparameternum )
01100 game_parameter[j] = gameParameterSettings[j].defaultValue;
01101 else
01102 game_parameter[j] = 0;
01103 game_parameter[num] = value;
01104 gameparameter_num = num + 1;
01105 }
01106 }
01107
01108 void GameMap :: setupResources ( void )
01109 {
01110 for ( int n = 0; n< 8; n++ ) {
01111 bi_resource[n].energy = 0;
01112 bi_resource[n].material = 0;
01113 bi_resource[n].fuel = 0;
01114
01115 #ifdef sgmain
01116
01117 for ( Player::BuildingList::iterator i = player[n].buildingList.begin(); i != player[n].buildingList.end(); i++ )
01118 for ( int r = 0; r < 3; r++ )
01119 if ( isResourceGlobal( r )) {
01120 bi_resource[n].resource(r) += (*i)->actstorage.resource(r);
01121 (*i)->actstorage.resource(r) = 0;
01122 }
01123 #endif
01124 }
01125 }
01126
01127
01128
01129
01130 int GameMap :: eventpassed( int saveas, int action, int mapid )
01131 {
01132 return eventpassed ( (action << 16) | saveas, mapid );
01133 }
01134
01135
01136
01137 int GameMap :: eventpassed( int id, int mapid )
01138 {
01139 return 0;
01140 }
01141
01142
01143 void GameMap:: IDManager :: registerUnitNetworkID( Vehicle* veh )
01144 {
01145 vehicleLookupCache[veh->networkid] = veh;
01146 if ( unitnetworkid < veh->networkid )
01147 unitnetworkid = veh->networkid;
01148 }
01149
01150 void GameMap:: IDManager :: unregisterUnitNetworkID( Vehicle* veh )
01151 {
01152 VehicleLookupCache::iterator j = vehicleLookupCache.find(veh->networkid);
01153 if ( j != vehicleLookupCache.end() )
01154 vehicleLookupCache.erase(j);
01155 }
01156
01157
01158 int GameMap :: IDManager :: getNewNetworkID()
01159 {
01160 ++unitnetworkid;
01161 return unitnetworkid;
01162 }
01163
01164 void GameMap :: IDManager :: readData ( tnstream& stream )
01165 {
01166 unitnetworkid = stream.readInt();
01167 }
01168
01169 void GameMap :: IDManager :: writeData ( tnstream& stream ) const
01170 {
01171 stream.writeInt( unitnetworkid );
01172 }
01173
01174 Vehicle* GameMap :: getUnit ( Vehicle* eht, int nwid )
01175 {
01176 if ( !eht )
01177 return NULL;
01178 else {
01179 if ( eht->networkid == nwid )
01180 return eht;
01181 else
01182 for ( ContainerBase::Cargo::const_iterator i = eht->getCargo().begin(); i != eht->getCargo().end(); ++i )
01183 if ( *i ) {
01184 if ( (*i)->networkid == nwid )
01185 return *i;
01186 else {
01187 Vehicle* ld = getUnit ( *i, nwid );
01188 if ( ld )
01189 return ld;
01190 }
01191 }
01192 return NULL;
01193 }
01194 }
01195
01196
01197
01198 Vehicle* GameMap :: getUnit ( int nwid, bool consistencyCheck )
01199 {
01200 IDManager::VehicleLookupCache::iterator i = idManager.vehicleLookupCache.find( nwid );
01201 if ( i != idManager.vehicleLookupCache.end() )
01202 return i->second;
01203
01204
01205 if ( consistencyCheck )
01206 for ( int p = 0; p < 9; p++ )
01207 for ( Player::VehicleList::iterator i = player[p].vehicleList.begin(); i != player[p].vehicleList.end(); i++ )
01208 if ( (*i)->networkid == nwid ) {
01209 displaymessage("warning: id not registered in VehicleLookupCache!",1);
01210 return *i;
01211 }
01212
01213 return NULL;
01214 }
01215
01216 const Vehicle* GameMap :: getUnit ( int nwid, bool consistencyCheck ) const
01217 {
01218 IDManager::VehicleLookupCache::const_iterator i = idManager.vehicleLookupCache.find( nwid );
01219 if ( i != idManager.vehicleLookupCache.end() )
01220 return i->second;
01221
01222
01223 if ( consistencyCheck )
01224 for ( int p = 0; p < 9; p++ )
01225 for ( Player::VehicleList::const_iterator i = player[p].vehicleList.begin(); i != player[p].vehicleList.end(); i++ )
01226 if ( (*i)->networkid == nwid ) {
01227 displaymessage("warning: id not registered in VehicleLookupCache!",1);
01228 return *i;
01229 }
01230
01231 return NULL;
01232 }
01233
01234 Vehicle* GameMap :: getUnit ( int x, int y, int nwid )
01235 {
01236 MapField* fld = getField ( x, y );
01237 if ( !fld )
01238 return NULL;
01239
01240 if ( fld->vehicle && fld->vehicle->networkid == nwid )
01241 return fld->vehicle;
01242
01243 if ( fld->getContainer() )
01244 return fld->getContainer()->findUnit( nwid );
01245
01246 return NULL;
01247
01248 }
01249
01250 ContainerBase* GameMap::getContainer ( int nwid )
01251 {
01252 if ( nwid > 0 )
01253 return getUnit(nwid);
01254 else {
01255 int x = (-nwid) & 0xffff;
01256 int y = (-nwid) >> 16;
01257 MapField* fld = getField(x,y);
01258 if ( !fld )
01259 return NULL;
01260
01261 return fld->building;
01262 }
01263 }
01264
01265 const ContainerBase* GameMap::getContainer ( int nwid ) const
01266 {
01267 if ( nwid > 0 )
01268 return getUnit(nwid);
01269 else {
01270 int x = (-nwid) & 0xffff;
01271 int y = (-nwid) >> 16;
01272 const MapField* fld = getField(x,y);
01273 if ( !fld )
01274 return NULL;
01275
01276 return fld->building;
01277 }
01278 }
01279
01280
01281
01282 void GameMap::beginTurn()
01283 {
01284 if ( !player[actplayer].exist() )
01285 if ( replayinfo )
01286 if ( replayinfo->guidata[actplayer] ) {
01287 delete replayinfo->guidata[actplayer];
01288 replayinfo->guidata[actplayer] = NULL;
01289 }
01290
01291 for ( Player::VehicleList::const_iterator i = player[actplayer].vehicleList.begin(); i != player[actplayer].vehicleList.end(); i++ )
01292 (*i)->beginTurn();
01293
01294 if ( player[actplayer].exist() && player[actplayer].stat != Player::off )
01295 sigPlayerTurnBegins( player[actplayer] );
01296
01297 }
01298
01299
01300 void GameMap::endTurn()
01301 {
01302
01303 player[actplayer].ASCversion = getNumericVersion();
01304 Player::PlayTime pt;
01305 pt.turn = time.turn();
01306 ::time ( &pt.date );
01307 player[actplayer].playTime.push_back ( pt );
01308
01309 sigPlayerTurnEnds( player[actplayer] );
01310 sigPlayerTurnEndsStatic( this, player[actplayer] );
01311
01312 actions.breakUndo();
01313
01314 for ( int i = 0; i < 9; ++i )
01315 for ( Player::BuildingList::iterator v = player[i].buildingList.begin(); v != player[i].buildingList.end(); ++v ) {
01316 if ( i == actplayer )
01317 (*v)->endOwnTurn();
01318 (*v)->endAnyTurn();
01319 }
01320
01321
01322 Player::VehicleList toRemove;
01323 for ( Player::VehicleList::iterator v = player[actplayer].vehicleList.begin(); v != player[actplayer].vehicleList.end(); ++v ) {
01324 Vehicle* actvehicle = *v;
01325
01326
01327
01328 if (( actvehicle->height >= chtieffliegend ) && ( actvehicle->height <= chhochfliegend ) && ( getField(actvehicle->xpos,actvehicle->ypos)->vehicle == actvehicle)) {
01329 if ( getmaxwindspeedforunit ( actvehicle ) < weather.windSpeed*maxwindspeed ){
01330 new Message ( getUnitReference( *v ) + " crashed because of the strong wind", this, 1<<(*v)->getOwner());
01331 toRemove.push_back ( *v );
01332 } else {
01333
01334 int j = actvehicle->getTank().fuel - UnitHooveringLogic::calcFuelUsage( actvehicle );
01335 if (j < 0) {
01336 new Message ( getUnitReference( *v ) + " crashed due to lack of fuel", this, 1<<(*v)->getOwner());
01337 toRemove.push_back ( *v );
01338 } else {
01339 actvehicle->getResource( actvehicle->getTank().fuel - j, Resources::Fuel, false);
01340 }
01341 }
01342 }
01343
01344 if ( actvehicle )
01345 actvehicle->endOwnTurn();
01346
01347 }
01348
01349 for ( Player::VehicleList::iterator v = toRemove.begin(); v != toRemove.end(); v++ )
01350 delete *v;
01351
01352 checkunitsforremoval( this );
01353
01354 for ( int i = 0; i < 9; ++i )
01355 for ( Player::VehicleList::iterator v = player[i].vehicleList.begin(); v != player[i].vehicleList.end(); ++v )
01356 (*v)->endAnyTurn();
01357
01358
01359 if ( replayinfo )
01360 replayinfo->closeLogging();
01361
01362 processJournal();
01363
01364 sigPlayerTurnHasEnded( player[actplayer] );
01365
01366 }
01367
01368
01369 void GameMap::endRound()
01370 {
01371 actplayer = 0;
01372 time.set ( time.turn()+1, 0 );
01373
01374 for ( int y = 0; y < ysize; ++y )
01375 for ( int x = 0; x < xsize; ++x )
01376 getField(x,y)->endRound( time.turn() );
01377
01378 for (int i = 0; i <= 7; i++) {
01379 if (player[i].exist() && player[i].stat != Player::off ) {
01380
01381 for ( Player::VehicleList::iterator j = player[i].vehicleList.begin(); j != player[i].vehicleList.end(); j++ )
01382 (*j)->endRound();
01383
01384 for ( Player::BuildingList::iterator j = player[i].buildingList.begin(); j != player[i].buildingList.end(); j++ )
01385 (*j)->endRound();
01386
01387 typedef PointerList<ContainerBase::Work*> BuildingWork;
01388 BuildingWork buildingWork;
01389
01390 for ( Player::BuildingList::iterator j = player[i].buildingList.begin(); j != player[i].buildingList.end(); j++ ) {
01391 if ( (*j)->getEntry().x == 19 && (*j)->getEntry().y == 72 )
01392 logMessage("dummy", "dummy");
01393 ContainerBase::Work* w = (*j)->spawnWorkClasses( false );
01394 if ( w ) {
01395 buildingWork.push_back ( w );
01396 logMessage("ResourceWork", "Building " + (*j)->getEntry().toString() + " has work to do");
01397 } else
01398 logMessage("ResourceWork", "Building " + (*j)->getEntry().toString() + " has no work to do");
01399 }
01400
01401 for ( Player::VehicleList::iterator j = player[i].vehicleList.begin(); j != player[i].vehicleList.end(); j++ ) {
01402 ContainerBase::Work* w = (*j)->spawnWorkClasses( false );
01403 if ( w ) {
01404 buildingWork.push_back ( w );
01405
01406 }
01407
01408 }
01409
01410
01411 bool didSomething;
01412 do {
01413 didSomething = false;
01414 for ( BuildingWork::iterator j = buildingWork.begin(); j != buildingWork.end(); j++ ) {
01415 if ( ! (*j)->finished() )
01416 if ( (*j)->run() )
01417 didSomething = true;
01418 }
01419 } while ( didSomething );
01420 doresearch( this, i );
01421 }
01422 }
01423
01424 int playerMask = 0;
01425 for ( int i = 0; i < getPlayerCount(); ++i )
01426 if ( !getPlayer(i).exist() )
01427 playerMask |= 1 << i;
01428 MapField::resetView(this,playerMask);
01429
01430 if ( getgameparameter( cgp_objectGrowthMultiplier) > 0 )
01431 objectGrowth();
01432 }
01433
01434 #include "libs/rand/rand_r.h"
01435 #include "libs/rand/rand_r.c"
01436
01437 int GameMap::random( int max )
01438 {
01439 return asc_rand_r( &randomSeed ) % max;
01440 }
01441
01442 void GameMap::objectGrowth()
01443 {
01444 typedef vector< pair<MapField*,int> > NewObjects;
01445 map<MapField*,int> remainingGrowthTime;
01446
01447 NewObjects newObjects;
01448 for ( int y = 0; y < ysize; ++y )
01449 for ( int x = 0; x < xsize; ++x ) {
01450 MapField* fld = getField( x, y );
01451 for ( MapField::ObjectContainer::iterator i = fld->objects.begin(); i != fld->objects.end(); ++i)
01452 if ( i->typ->growthRate > 0 )
01453 for ( int d = 0; d < 6; ++d ) {
01454 MapField* fld2 = getField ( getNeighbouringFieldCoordinate( MapCoordinate(x,y), d ));
01455 if ( fld2 )
01456 if ( i->typ->growOnUnits || ((!fld2->vehicle || fld2->vehicle->height >= chtieffliegend) && !fld2->building ))
01457 if ( fld2->objects.empty() || getgameparameter( cgp_objectGrowOnOtherObjects ) > 0 )
01458 if ( i->remainingGrowthTime > 0 || i->typ->growthDuration <= 0 ) {
01459 double d = i->typ->growthRate * getgameparameter( cgp_objectGrowthMultiplier) / 100;
01460 if ( d > 0 ) {
01461 if ( d > 0.9 )
01462 d = 0.9;
01463
01464 int p = static_cast<int>(std::ceil ( double(1) / d));
01465 if ( p > 1 )
01466 if ( random ( p ) == 1 )
01467 if ( i->typ->fieldModification[fld2->getWeather()].terrainaccess.accessible( fld2->bdt) > 0 ) {
01468 newObjects.push_back( make_pair( fld2, i->typ->id ));
01469 i->remainingGrowthTime -= 1;
01470 remainingGrowthTime[fld2] = i->remainingGrowthTime;
01471 }
01472 }
01473 }
01474 }
01475 }
01476
01477 for ( NewObjects::iterator i = newObjects.begin(); i != newObjects.end(); ++i )
01478 if ( !i->first->checkForObject( getobjecttype_byid( i->second ))) {
01479 if ( i->first->addobject ( getobjecttype_byid( i->second ))) {
01480 Object* o = i->first->checkForObject( getobjecttype_byid( i->second ));
01481 assert(o);
01482 o->remainingGrowthTime = remainingGrowthTime[i->first];
01483 }
01484 }
01485
01486 checkunitsforremoval( this );
01487 }
01488
01489 SigC::Signal1<void,GameMap&> GameMap::sigMapDeletion;
01490 SigC::Signal1<void,GameMap&> GameMap::sigMapCreation;
01491 SigC::Signal2<void,GameMap*,Player&> GameMap::sigPlayerTurnEndsStatic;
01492
01493 GameMap :: ~GameMap ()
01494 {
01495 sigMapDeletion( *this );
01496 state = Destruction;
01497
01498 if ( field )
01499
01500 for ( int l=0 ;l < xsize * ysize ; l++ ) {
01501 if ( (field[l].bdt & getTerrainBitType(cbbuildingentry)).any() )
01502 delete field[l].building;
01503
01504
01505 Vehicle* aktvehicle = field[l].vehicle;
01506 if ( aktvehicle )
01507 delete aktvehicle;
01508
01509 }
01510
01511 int i;
01512 for ( i = 0; i <= 8; i++) {
01513 if ( player[i].ai ) {
01514 delete player[i].ai;
01515 player[i].ai = NULL;
01516 }
01517 }
01518
01519 if ( replayinfo ) {
01520 delete replayinfo;
01521 replayinfo = NULL;
01522 }
01523
01524 if ( game_parameter ) {
01525 delete[] game_parameter;
01526 game_parameter = NULL;
01527 }
01528
01529 if ( network ) {
01530 delete network;
01531 network = NULL;
01532 }
01533
01534 if ( actionRecorder ) {
01535 delete actionRecorder;
01536 actionRecorder = NULL;
01537 }
01538
01539 #ifdef WEATHERGENERATOR
01540 delete weatherSystem;
01541 #endif
01542
01543 if ( field ) {
01544 delete[] field;
01545 field = NULL;
01546 }
01547
01548 delete tasks;
01549 tasks = NULL;
01550
01551 }
01552
01553
01554
01555
01556
01557
01558
01559
01560
01561
01562
01563
01564
01565 bool GameMap :: ResourceTribute :: empty ( )
01566 {
01567 for (int i = 0; i < 8; i++)
01568 for (int j = 0; j < 8; j++)
01569 for (int k = 0; k < 3; k++) {
01570 if ( avail[i][j].resource(k) )
01571 return false;
01572 if ( paid[i][j].resource(k) )
01573 return false;
01574 }
01575
01576 return true;
01577 }
01578
01579 const int tributeVersion = 1;
01580
01581 void GameMap :: ResourceTribute :: read ( tnstream& stream )
01582 {
01583 int version = stream.readInt();
01584 bool noVersion;
01585
01586 if ( -version >= tributeVersion ) {
01587 for ( int a = 0; a < 8; ++a )
01588 for ( int b = 0; b < 8; ++b )
01589 payStatusLastTurn[a][b].read( stream );
01590 noVersion = false;
01591 } else
01592 noVersion = true;
01593
01594
01595 for ( int a = 0; a < 3; a++ )
01596 for ( int b = 0; b < 8; b++ )
01597 for ( int c = 0; c < 8; c++ ) {
01598 if ( noVersion ) {
01599 avail[b][c].resource(a) = version;
01600 noVersion = false;
01601 } else
01602 avail[b][c].resource(a) = stream.readInt();
01603 }
01604
01605 for ( int a = 0; a < 3; a++ )
01606 for ( int b = 0; b < 8; b++ )
01607 for ( int c = 0; c < 8; c++ )
01608 paid[b][c].resource(a) = stream.readInt();
01609 }
01610
01611 void GameMap :: ResourceTribute :: write ( tnstream& stream )
01612 {
01613 stream.writeInt ( -tributeVersion );
01614 for ( int a = 0; a < 8; a++ )
01615 for ( int b = 0; b < 8; b++ )
01616 payStatusLastTurn[a][b].write( stream );
01617
01618 for ( int a = 0; a < 3; a++ )
01619 for ( int b = 0; b < 8; b++ )
01620 for ( int c = 0; c < 8; c++ )
01621 stream.writeInt ( avail[b][c].resource(a) );
01622
01623
01624 for ( int a = 0; a < 3; a++ )
01625 for ( int b = 0; b < 8; b++ )
01626 for ( int c = 0; c < 8; c++ )
01627 stream.writeInt ( paid[b][c].resource(a) );
01628 }
01629
01630
01631
01632 int GameMap::resize( int top, int bottom, int left, int right )
01633 {
01634 if ( !top && !bottom && !left && !right )
01635 return 0;
01636
01637 if ( -(top + bottom) > ysize )
01638 return 1;
01639
01640 if ( -(left + right) > xsize )
01641 return 2;
01642
01643 if ( bottom & 1 || top & 1 )
01644 return 3;
01645
01646
01647 int ox1, oy1, ox2, oy2;
01648
01649 if ( top < 0 ) {
01650 for ( int x = 0; x < xsize; x++ )
01651 for ( int y = 0; y < -top; y++ )
01652 getField(x,y)->deleteeverything();
01653
01654 oy1 = -top;
01655 } else
01656 oy1 = 0;
01657
01658 if ( bottom < 0 ) {
01659 for ( int x = 0; x < xsize; x++ )
01660 for ( int y = ysize+bottom; y < ysize; y++ )
01661 getField(x,y)->deleteeverything();
01662
01663 oy2 = ysize + bottom;
01664 } else
01665 oy2 = ysize;
01666
01667 if ( left < 0 ) {
01668 for ( int x = 0; x < -left; x++ )
01669 for ( int y = 0; y < ysize; y++ )
01670 getField(x,y)->deleteeverything();
01671 ox1 = -left;
01672 } else
01673 ox1 = 0;
01674
01675 if ( right < 0 ) {
01676 for ( int x = xsize+right; x < xsize; x++ )
01677 for ( int y = 0; y < ysize; y++ )
01678 getField(x,y)->deleteeverything();
01679 ox2 = xsize + right;
01680 } else
01681 ox2 = xsize;
01682
01683 for (int s = 0; s < 9; s++)
01684 for ( Player::BuildingList::iterator i = player[s].buildingList.begin(); i != player[s].buildingList.end(); i++ )
01685 (*i)->unchainbuildingfromfield();
01686
01687
01688 int newx = xsize + left + right;
01689 int newy = ysize + top + bottom;
01690
01691 MapField* newfield = new MapField [ newx * newy ];
01692 for ( int i = 0; i < newx * newy; i++ )
01693 newfield[i].setMap ( this );
01694
01695 int x;
01696 for ( x = ox1; x < ox2; x++ )
01697 for ( int y = oy1; y < oy2; y++ ) {
01698 MapField* org = getField ( x, y );
01699 MapField* dst = &newfield[ (x + left) + ( y + top ) * newx];
01700 *dst = *org;
01701 }
01702
01703 MapField defaultfield;
01704 defaultfield.setMap ( this );
01705 defaultfield.typ = getterraintype_byid ( 30 )->weather[0];
01706
01707 for ( x = 0; x < left; x++ )
01708 for ( int y = 0; y < newy; y++ )
01709 newfield[ x + y * newx ] = defaultfield;
01710
01711 for ( x = xsize + left; x < xsize + left + right; x++ )
01712 for ( int y = 0; y < newy; y++ )
01713 newfield[ x + y * newx ] = defaultfield;
01714
01715
01716 int y;
01717 for ( y = 0; y < top; y++ )
01718 for ( int x = 0; x < newx; x++ )
01719 newfield[ x + y * newx ] = defaultfield;
01720
01721 for ( y = ysize + top; y < ysize + top + bottom; y++ )
01722 for ( int x = 0; x < newx; x++ )
01723 newfield[ x + y * newx ] = defaultfield;
01724
01725 calculateallobjects( this );
01726
01727 for ( int p = 0; p < newx*newy; p++ )
01728 newfield[p].setparams();
01729
01730 delete[] field;
01731 field = newfield;
01732 xsize = newx;
01733 ysize = newy;
01734
01735
01736 for (int s = 0; s < 9; s++)
01737 for ( Player::BuildingList::iterator i = player[s].buildingList.begin(); i != player[s].buildingList.end(); i++ ) {
01738 MapCoordinate mc = (*i)->getEntry();
01739 mc.x += left;
01740 mc.y += top;
01741 (*i)->chainbuildingtofield ( mc );
01742 }
01743
01744 for (int s = 0; s < 9; s++)
01745 for ( Player::VehicleList::iterator i = player[s].vehicleList.begin(); i != player[s].vehicleList.end(); i++ ) {
01746 (*i)->xpos += left;
01747 (*i)->ypos += top;
01748 }
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758 overviewMapHolder.resetSize();
01759
01760 if ( left || top )
01761 sigCoordinateShift( MapCoodinateVector(left,top));
01762
01763 return 0;
01764 }
01765
01766 pterraintype GameMap :: getterraintype_byid ( int id )
01767 {
01768 return terrainTypeRepository.getObject_byID ( id );
01769 }
01770
01771 ObjectType* GameMap :: getobjecttype_byid ( int id )
01772 {
01773 return objectTypeRepository.getObject_byID ( id );
01774 }
01775
01776 const ObjectType* GameMap :: getobjecttype_byid ( int id ) const
01777 {
01778 return objectTypeRepository.getObject_byID ( id );
01779 }
01780
01781
01782 VehicleType* GameMap :: getvehicletype_byid ( int id )
01783 {
01784 return vehicleTypeRepository.getObject_byID ( id );
01785 }
01786
01787 const VehicleType* GameMap :: getvehicletype_byid ( int id ) const
01788 {
01789 return vehicleTypeRepository.getObject_byID ( id );
01790 }
01791
01792
01793 BuildingType* GameMap :: getbuildingtype_byid ( int id )
01794 {
01795 return buildingTypeRepository.getObject_byID ( id );
01796 }
01797
01798 const BuildingType* GameMap :: getbuildingtype_byid ( int id ) const
01799 {
01800 return buildingTypeRepository.getObject_byID ( id );
01801 }
01802
01803 const Technology* GameMap :: gettechnology_byid ( int id )
01804 {
01805 return technologyRepository.getObject_byID ( id );
01806 }
01807
01808
01809 pterraintype GameMap :: getterraintype_bypos ( int pos )
01810 {
01811 return terrainTypeRepository.getObject_byPos ( pos );
01812 }
01813
01814 ObjectType* GameMap :: getobjecttype_bypos ( int pos )
01815 {
01816 return objectTypeRepository.getObject_byPos ( pos );
01817 }
01818
01819 VehicleType* GameMap :: getvehicletype_bypos ( int pos )
01820 {
01821 return vehicleTypeRepository.getObject_byPos ( pos );
01822 }
01823
01824 BuildingType* GameMap :: getbuildingtype_bypos ( int pos )
01825 {
01826 return buildingTypeRepository.getObject_byPos ( pos );
01827 }
01828
01829 const Technology* GameMap :: gettechnology_bypos ( int pos )
01830 {
01831 return technologyRepository.getObject_byPos ( pos );
01832 }
01833
01834 int GameMap :: getTerrainTypeNum ( )
01835 {
01836 return terrainTypeRepository.getNum();
01837 }
01838
01839 int GameMap :: getObjectTypeNum ( )
01840 {
01841 return objectTypeRepository.getNum();
01842 }
01843
01844 int GameMap :: getVehicleTypeNum ( )
01845 {
01846 return vehicleTypeRepository.getNum();
01847 }
01848
01849 int GameMap :: getBuildingTypeNum ( )
01850 {
01851 return buildingTypeRepository.getNum();
01852 }
01853
01854 int GameMap :: getTechnologyNum ( )
01855 {
01856 return technologyRepository.getNum();
01857 }
01858
01859 void GameMap::processJournal()
01860 {
01861 if ( !newJournal.empty() ) {
01862 ASCString add = gameJournal;
01863
01864 char tempstring[100];
01865 char tempstring2[100];
01866 sprintf( tempstring, "#color0# %s ; turn %d #color0##crt##crt#", player[actplayer].getName().c_str(), time.turn() );
01867 sprintf( tempstring2, "#color%d#", getplayercolor ( actplayer ));
01868
01869 int fnd;
01870 do {
01871 fnd = 0;
01872 if ( !add.empty() ) {
01873 if ( add.find ( '\n', add.length()-1 ) != add.npos ) {
01874 add.erase ( add.length()-1 );
01875 fnd++;
01876 } else
01877 if ( add.length() > 4 )
01878 if ( add.find ( "#crt#", add.length()-5 ) != add.npos ) {
01879 add.erase ( add.length()-5 );
01880 fnd++;
01881 }
01882 }
01883
01884 } while ( fnd );
01885
01886 add += tempstring2;
01887 add += newJournal;
01888 add += tempstring;
01889
01890 gameJournal = add;
01891 newJournal.erase();
01892
01893 lastjournalchange.set ( time.turn(), actplayer );
01894 }
01895
01896 }
01897
01898
01899 void GameMap :: startGame ( )
01900 {
01901 time.set ( 1, 0 );
01902
01903 for ( int j = 0; j < 8; j++ )
01904 player[j].queuedEvents = 1;
01905
01906 levelfinished = false;
01907
01908 for ( int n = 0; n< 8; n++ ) {
01909 bi_resource[n].energy = 0;
01910 bi_resource[n].material = 0;
01911 bi_resource[n].fuel = 0;
01912 player[n].research.setMultiplier( getgameparameter(cgp_researchOutputMultiplier) );
01913 }
01914
01915
01916
01917 #ifndef karteneditor
01918 actplayer = -1;
01919 #else
01920 actplayer = 0;
01921 #endif
01922
01923 setupResources();
01924
01925
01926 newRound();
01927 }
01928
01929 bool GameMap::UnitProduction::check ( int id )
01930 {
01931 for ( GameMap::UnitProduction::IDsAllowed::iterator i = idsAllowed.begin(); i != idsAllowed.end(); i ++ )
01932 if( *i == id )
01933 return true;
01934
01935 return false;
01936 }
01937
01938 VisibilityStates GameMap::getInitialMapVisibility( int player )
01939 {
01940 VisibilityStates c = VisibilityStates( getgameparameter ( cgp_initialMapVisibility ));
01941
01942 if ( this->player[player].ai ) {
01943 if ( this->player[player].ai->isRunning() ) {
01944 if ( c < this->player[player].ai->getVision() )
01945 c = this->player[player].ai->getVision();
01946 } else
01947
01948 if ( c < visible_ago )
01949 c = visible_ago;
01950 }
01951 return c;
01952 }
01953
01954 void GameMap::setPlayerMode( Player& p, State s )
01955 {
01956 p.getParentMap()->state = s;
01957 }
01958
01959 int GameMap::getMemoryFootprint() const
01960 {
01961 int size = sizeof(*this);
01962 if( replayinfo )
01963 for ( int i = 0; i < 8; ++i ) {
01964 if ( replayinfo->guidata[i] )
01965 size += replayinfo->guidata[i]->getMemoryFootprint();
01966 if ( replayinfo->map[i] )
01967 size += replayinfo->map[i]->getMemoryFootprint();
01968 }
01969 return size;
01970 }
01971
01972
01973
01974 void GameMap::operator= ( const GameMap& map )
01975 {
01976 throw ASCmsgException ( "GameMap::operator= undefined");
01977 }
01978
01979
01980 void AiThreat :: write ( tnstream& stream )
01981 {
01982 const int version = 1000;
01983 stream.writeInt ( version );
01984 stream.writeInt ( threatTypes );
01985 for ( int i = 0; i < threatTypes; i++ )
01986 stream.writeInt ( threat[i] );
01987 }
01988
01989 void AiThreat:: read ( tnstream& stream )
01990 {
01991
01992 int version = stream.readInt();
01993 if ( version == 1000 ) {
01994 threatTypes = stream.readInt();
01995 for ( int i = 0; i < threatTypes; i++ )
01996 threat[i] = stream.readInt();
01997 }
01998 }
01999
02000
02001 void AiValue :: write ( tnstream& stream )
02002 {
02003 const int version = 2000;
02004 stream.writeInt ( version );
02005 stream.writeInt ( value );
02006 stream.writeInt ( addedValue );
02007 threat.write ( stream );
02008 stream.writeInt ( valueType );
02009 }
02010
02011 void AiValue:: read ( tnstream& stream )
02012 {
02013 int version = stream.readInt();
02014 if ( version == 2000 ) {
02015 value = stream.readInt ( );
02016 addedValue= stream.readInt ( );
02017 threat.read ( stream );
02018 valueType = stream.readInt ( );
02019 }
02020 }
02021
02022 const int aiParamVersion = 3002;
02023
02024 void AiParameter::write ( tnstream& stream )
02025 {
02026 stream.writeInt ( aiParamVersion );
02027 stream.writeInt ( lastDamage );
02028 stream.writeInt ( damageTime.abstime );
02029 stream.writeInt ( dest.x );
02030 stream.writeInt ( dest.y );
02031 stream.writeInt ( dest.getNumericalHeight() );
02032 stream.writeInt ( dest_nwid );
02033 stream.writeInt ( data );
02034 AiValue::write( stream );
02035 stream.writeInt ( task );
02036 stream.writeInt ( jobPos );
02037 stream.writeInt ( jobs.size() );
02038 for ( int i = 0; i < jobs.size(); i++ )
02039 stream.writeInt( jobs[i] );
02040 stream.writeInt ( resetAfterJobCompletion );
02041 }
02042
02043 void AiParameter::read ( tnstream& stream )
02044 {
02045 int version = stream.readInt();
02046 if ( version >= 3000 && version <= aiParamVersion ) {
02047 lastDamage = stream.readInt();
02048 damageTime.abstime = stream.readInt();
02049 int x = stream.readInt();
02050 int y = stream.readInt();
02051 int z = stream.readInt();
02052 dest.setnum ( x, y, z );
02053 dest_nwid = stream.readInt();
02054 data = stream.readInt();
02055 AiValue::read( stream );
02056 task = (Task) stream.readInt();
02057 if ( version == 3000 ) {
02058 jobs.clear();
02059 jobs.push_back ( Job( stream.readInt() ));
02060 } else {
02061 jobPos = stream.readInt();
02062 int num = stream.readInt();
02063 jobs.clear();
02064 for ( int i = 0; i < num; i++ )
02065 jobs.push_back ( Job( stream.readInt() ));
02066 }
02067 if ( version >= 3002 )
02068 resetAfterJobCompletion = stream.readInt();
02069 else
02070 resetAfterJobCompletion = false;
02071 }
02072 }
02073
02074 void AiThreat :: reset ( void )
02075 {
02076 for ( int i = 0; i < aiValueTypeNum; i++ )
02077 threat[i] = 0;
02078 }
02079
02080 AiParameter :: AiParameter ( Vehicle* _unit ) : AiValue ( getFirstBit( _unit->height ))
02081 {
02082 reset( _unit );
02083 }
02084
02085
02086 void AiParameter :: resetTask ( )
02087 {
02088 dest.setnum ( -1, -1, -1 );
02089 dest_nwid = -1;
02090 task = tsk_nothing;
02091 }
02092
02093 void AiParameter::addJob ( Job j, bool front )
02094 {
02095 if ( front )
02096 jobs.insert ( jobs.begin(), j );
02097 else
02098 jobs.push_back ( j );
02099 }
02100
02101 void AiParameter::setJob ( const JobList& jobs )
02102 {
02103 this->jobs = jobs;
02104 }
02105
02106 void AiParameter::setJob ( Job j )
02107 {
02108 int pos = 0;
02109 for ( JobList::iterator i = jobs.begin(); i != jobs.end(); ++i, ++pos )
02110 if ( *i == j ) {
02111 jobPos = pos;
02112 return;
02113 }
02114
02115 addJob ( j, true );
02116 }
02117
02118
02119 bool AiParameter::hasJob ( AiParameter::Job j )
02120 {
02121 return find ( jobs.begin(), jobs.end(), j ) != jobs.end();
02122 }
02123
02124 void AiParameter :: setNewHeight()
02125 {
02126 AiValue::reset ( getFirstBit( unit->height ) );
02127 }
02128
02129
02130 void AiParameter :: reset ( Vehicle* _unit )
02131 {
02132 unit = _unit;
02133 lastDamage = unit->damage;
02134 AiValue::reset ( getFirstBit( _unit->height ) );
02135 data = 0;
02136
02137 clearJobs();
02138 resetTask();
02139 resetAfterJobCompletion = false;
02140 }
02141
02142 void AiParameter :: setNextJob()
02143 {
02144 jobPos++;
02145 }
02146
02147 void AiParameter :: restartJobs()
02148 {
02149 jobPos = 0;
02150 }
02151
02152 void AiParameter :: clearJobs()
02153 {
02154 jobs.clear();
02155 jobPos = 0;
02156 }
02157
02158
02159
02160
02161 GameMap :: ReplayInfo :: ReplayInfo ( void )
02162 {
02163 for (int i = 0; i < 8; i++) {
02164 guidata[i] = NULL;
02165 map[i] = NULL;
02166 }
02167 actmemstream = NULL;
02168 stopRecordingActions = 0;
02169 }
02170
02171 void GameMap :: ReplayInfo :: read ( tnstream& stream )
02172 {
02173 bool loadgui[8];
02174 bool loadmap[8];
02175
02176 for ( int i = 0; i < 8; i++ )
02177 loadgui[i] = stream.readInt();
02178
02179 for ( int i = 0; i < 8; i++ )
02180 loadmap[i] = stream.readInt();
02181
02182 stream.readInt();
02183
02184 for ( int i = 0; i < 8; i++ ) {
02185 if ( loadgui[i] ) {
02186 guidata[i] = new MemoryStreamStorage;
02187 guidata[i]->readfromstream ( &stream );
02188 } else
02189 guidata[i] = NULL;
02190
02191 if ( loadmap[i] ) {
02192 map[i] = new MemoryStreamStorage;
02193 map[i]->readfromstream ( &stream );
02194 } else
02195 map[i] = NULL;
02196 }
02197
02198 actmemstream = NULL;
02199 }
02200
02201 void GameMap :: ReplayInfo :: write ( tnstream& stream )
02202 {
02203 for ( int i = 0; i < 8; i++ )
02204 stream.writeInt ( guidata[i] != NULL );
02205
02206 for ( int i = 0; i < 8; i++ )
02207 stream.writeInt ( map[i] != NULL );
02208
02209 stream.writeInt ( actmemstream != NULL );
02210
02211 for ( int i = 0; i < 8; i++ ) {
02212
02213 if ( guidata[i] )
02214 guidata[i]->writetostream ( &stream );
02215
02216 if ( map[i] )
02217 map[i]->writetostream ( &stream );
02218 }
02219 }
02220
02221 void GameMap :: ReplayInfo :: closeLogging()
02222 {
02223 if ( actmemstream ) {
02224 delete actmemstream;
02225 actmemstream = NULL;
02226 }
02227 }
02228
02229 GameMap :: ReplayInfo :: ~ReplayInfo ( )
02230 {
02231 for (int i = 0; i < 8; i++) {
02232 if ( guidata[i] ) {
02233 delete guidata[i];
02234 guidata[i] = NULL;
02235 }
02236 if ( map[i] ) {
02237 delete map[i];
02238 map[i] = NULL;
02239 }
02240 }
02241 if ( actmemstream ) {
02242 delete actmemstream ;
02243 actmemstream = NULL;
02244 }
02245 }
02246
02247 GameParameterSettings gameParameterSettings[gameparameternum ] = {
02248 { "LifetimeTrack", 1, 1, maxint, true, false, "lifetime of tracks"},
02249 { "LifetimeBrokenIce", 2, 1, maxint, true, false, "freezing time of icebreaker fairway"},
02250 { "MoveFromInaccessibleFields", 1, 0, 1, true, false, "move vehicles from unaccessible fields"},
02251 { "BuildingConstructionFactorMaterial", 100, 0, maxint, true, false, "building construction material factor (percent)"},
02252 { "BuildingConstructionFactorEnergy", 100, 0, maxint, true, false, "building construction fuel factor (percent)"},
02253 { "ForbidBuildingConstruction", 0, 0, 1, true, false, "forbid construction of buildings"},
02254 { "LimitUnitProductionByUnit", 0, 0, 2, true, false, "limit construction of units by other units"},
02255 { "Bi3Training", 0, 0, maxunitexperience, true, false, "use BI3 style training factor "},
02256 { "MaxMinesOnField", 1, 0, maxint, true, false, "maximum number of mines on a single field"},
02257 { "LifetimeAntipersonnelMine", 0, 0, maxint, true, false, "lifetime of antipersonnel mine"},
02258 { "LifetimeAntiTankMine", 0, 0, maxint, true, false, "lifetime of antitank mine"},
02259 { "LifetimeAntiSubMine", 0, 0, maxint, true, false, "lifetime of antisub mine"},
02260 { "LifetimeAntiShipMine", 0, 0, maxint, true, false, "lifetime of antiship mine"},
02261 { "BuildingArmorFactor", 100, 1, maxint, true, false, "building armor factor (percent)"},
02262 { "MaxBuildingRepair", 100, 0, 100, true, false, "max building damage repair / turn"},
02263 { "BuildingRepairCostIncrease", 100, 1, maxint, true, false, "building repair cost increase (percent)"},
02264 { "GlobalFuel", 1, 0, 1, true, false, "fuel globally available (BI Resource Mode)"},
02265 { "MaxTrainingExperience", maxunitexperience, 0, maxunitexperience, true, false, "maximum experience that can be gained by training"},
02266 { "InitialMapVisibility", 0, 0, 2, true, false, "initial map visibility"},
02267 { "AttackPower", 40, 1, 100, true, false, "attack power (EXPERIMENTAL!)"},
02268 { "JammingAmplifier", 100, 0, 1000, true, false, "jamming amplifier (EXPERIMENTAL!)"},
02269 { "JammingSlope", 10, 0, 100, true, false, "jamming slope (EXPERIMENTAL!)"},
02270 { "SupervisorMapSave", 0, 0, 1, false, false, "The Supervisor may save a game as new map (spying!!!)"},
02271 { "ObjectsDestroyedByTerrain", 1, 0, 1, true, false, "objects can be destroyed by terrain"},
02272 { "TrainingIncrement", 2, 1, maxunitexperience, true, false, "training centers: training increment"},
02273 { "ExperienceEffectDivisorAttack", 1, 1, 10, false, false, "experience effect divisor for attack"},
02274 { "DisableDirectView", 1, 0, 1, false, false, "disable direct View"},
02275 { "DisableUnitTrade", 0, 0, 1, false, false, "disable transfering units/buildings to other players"},
02276 { "ExperienceEffectDivisorDefense", 1, 1, 10, false, false, "experience effect divisor for defense"},
02277 { "DebugGameEvents", 0, 0, 2, true, false, "debug game events"},
02278 { "ObjectGrowthRate", 0, 0, maxint, true, false, "Object growth rate (percentage)" },
02279 { "ObjectsGrowOnOtherObjects", 1, 0, 1, false, false, "Objects can grow on fields with other objects" },
02280 { "ResearchOutputMultiplier", 1, 1, maxint, false, false, "Multiplies the research output of all labs" },
02281 { "ProduceOnlyResearchedStuffInternally", 0, 0, 1, true, false, "Produce only researched stuff internally" },
02282 { "ProduceOnlyResearchedStuffExternally", 1, 0, 1, true, false, "Produce only researched stuff externally" }
02283 };
02284