Index: trunk/serverevents.h =================================================================== --- trunk/serverevents.h (revision 2482) +++ trunk/serverevents.h (working copy) @@ -309,5 +309,10 @@ void sendRequestSaveInfo ( int saveingID ); void sendSavedReport ( sSavedReportMessage &savedReport, int player ); +void sendScore(cPlayer *Subject, int turn, cPlayer *Receiver = 0); +void sendNumEcos(cPlayer *Subject, cPlayer *Receiver = 0); +void sendUnitScore(cBuilding *); +void sendVictoryConditions(int turnLimit, int scoreLimit, cPlayer *receiver=0); + #endif // servereventsH Index: trunk/buildings.cpp =================================================================== --- trunk/buildings.cpp (revision 2482) +++ trunk/buildings.cpp (working copy) @@ -54,6 +54,7 @@ typ = b; owner = Owner; base = Base; + points = 0; if ( Owner == NULL || b == NULL ) { @@ -1262,8 +1263,12 @@ owner->ResearchCount++; owner->researchCentersWorkingOnArea[researchArea]++; } + + if( data.canScore ) + { + sendNumEcos(owner); + } - sendSubbaseValues(SubBase, owner->Nr); sendDoStartWork(this); } @@ -1351,6 +1356,11 @@ owner->ResearchCount--; owner->researchCentersWorkingOnArea[researchArea]--; } + + if( data.canScore ) + { + sendNumEcos(owner); + } sendSubbaseValues(SubBase, owner->Nr); sendDoStopWork(this); Index: trunk/menus.cpp =================================================================== --- trunk/menus.cpp (revision 2482) +++ trunk/menus.cpp (working copy) @@ -18,6 +18,7 @@ ***************************************************************************/ #include #include +#include #include "menus.h" #include "mouse.h" @@ -83,6 +84,19 @@ return ""; } +string sSettings::getVictoryConditionString() +{ + string r = iToStr(duration) + " "; + + switch(victoryType) + { + case SETTINGS_VICTORY_TURNS: r += lngPack.i18n("Text~Comp~Turns"); break; + case SETTINGS_VICTORY_POINTS: r += lngPack.i18n("Text~Comp~Points"); break; + case SETTINGS_VICTORY_ANNIHILATION: return lngPack.i18n("Text~Comp~NoLimit"); + } + return r; +} + cGameDataContainer::~cGameDataContainer() { if ( settings ) delete settings; @@ -128,7 +142,15 @@ } // init server - Server = new cServer( &serverMap, &serverPlayers, type, settings->gameType == SETTINGS_GAMETYPE_TURNS ); + int nTurns, nScore; + switch(settings->victoryType) + { + case SETTINGS_VICTORY_TURNS: nTurns = settings->duration; nScore = 0; break; + case SETTINGS_VICTORY_POINTS: nScore = settings->duration; nTurns = 0; break; + case SETTINGS_VICTORY_ANNIHILATION: nTurns = nScore = 0; break; + default: assert(0); + } + Server = new cServer( &serverMap, &serverPlayers, type, settings->gameType == SETTINGS_GAMETYPE_TURNS, nTurns, nScore ); // place resources for ( unsigned int i = 0; i < players.Size(); i++ ) @@ -992,6 +1014,8 @@ clansGroup->addButton ( new cMenuCheckButton ( position.x+240+64, position.y+iCurrentLine, lngPack.i18n( "Text~Option~Off"), settings.clans == SETTING_CLANS_OFF, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY ) ); menuItems.Add ( clansGroup ); iCurrentLine += iLineHeight*3; + + int tmpLine = iCurrentLine; // Credits field - this is where the money goes creditsLabel = new cMenuLabel ( position.x+64, position.y+iCurrentLine, lngPack.i18n ("Text~Title~Credits") +":" ); @@ -1012,8 +1036,43 @@ iCurrentLine += iLineHeight; creditsGroup->addButton ( new cMenuCheckButton ( position.x+140, position.y+iCurrentLine, lngPack.i18n( "Text~Option~Most") + " ("+iToStr(SETTING_CREDITS_MOST)+")", settings.credits == SETTING_CREDITS_MOST, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY ) ); menuItems.Add ( creditsGroup ); - iCurrentLine += iLineHeight; - + + iCurrentLine = tmpLine; + + // Victory condition + const bool bTurns = settings.victoryType == SETTINGS_VICTORY_TURNS; + const bool bPoints = settings.victoryType == SETTINGS_VICTORY_POINTS; + const bool bAnnih = settings.victoryType == SETTINGS_VICTORY_ANNIHILATION; + + const bool bShort = settings.duration == SETTINGS_DUR_SHORT; + const bool bMedi = settings.duration == SETTINGS_DUR_MEDIUM; + const bool bLong = settings.duration == SETTINGS_DUR_LONG; + + const std::string strTurns = lngPack.i18n("Text~Comp~Turns"); + const std::string strPoints = lngPack.i18n("Text~Comp~Points"); + const std::string strNoLimit = lngPack.i18n("Text~Comp~NoLimit"); + + victoryLabel = new cMenuLabel( + position.x+300, position.y+iCurrentLine, + lngPack.i18n("Text~Comp~GameEndsAt") + ); + menuItems.Add(victoryLabel ); + + tmpLine = iCurrentLine += iLineHeight; + + victoryGroup = new cMenuRadioGroup(); + victoryGroup->addButton(new cMenuCheckButton(position.x + 380, position.y+iCurrentLine, "100 "+strTurns, bTurns && bShort, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY)); iCurrentLine += iLineHeight; + victoryGroup->addButton(new cMenuCheckButton(position.x + 380, position.y+iCurrentLine, "200 "+strTurns, bTurns && bMedi, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY)); iCurrentLine += iLineHeight; + victoryGroup->addButton(new cMenuCheckButton(position.x + 380, position.y+iCurrentLine, "400 "+strTurns, bTurns && bLong, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY)); iCurrentLine += iLineHeight; + + iCurrentLine = tmpLine; + + victoryGroup->addButton(new cMenuCheckButton(position.x + 500, position.y+iCurrentLine, "100 "+strPoints, bPoints && bShort, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY)); iCurrentLine += iLineHeight; + victoryGroup->addButton(new cMenuCheckButton(position.x + 500, position.y+iCurrentLine, "200 "+strPoints, bPoints && bMedi, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY)); iCurrentLine += iLineHeight; + victoryGroup->addButton(new cMenuCheckButton(position.x + 500, position.y+iCurrentLine, "400 "+strPoints, bPoints && bLong, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY)); iCurrentLine += iLineHeight; + + victoryGroup->addButton(new cMenuCheckButton(position.x + 440, position.y+iCurrentLine, strNoLimit, bAnnih, true, cMenuCheckButton::RADIOBTN_TYPE_TEXT_ONLY)); iCurrentLine += iLineHeight; + menuItems.Add(victoryGroup); } cSettingsMenu::~cSettingsMenu() @@ -1031,6 +1090,7 @@ delete clansLabel; delete resFrequencyLabel; delete gameTypeLabel; + delete victoryLabel; delete metalGroup; delete oilGroup; @@ -1041,6 +1101,7 @@ delete clansGroup; delete resFrequencyGroup; delete gameTypeGroup; + delete victoryGroup; } void cSettingsMenu::backReleased( void* parent ) @@ -1121,6 +1182,26 @@ if ( gameTypeGroup->buttonIsChecked ( 0 ) ) settings.gameType = SETTINGS_GAMETYPE_TURNS; else settings.gameType = SETTINGS_GAMETYPE_SIMU; + + if(victoryGroup->buttonIsChecked(7)) + settings.victoryType = SETTINGS_VICTORY_ANNIHILATION; + else for(int i=0; i<6; i++) + { + if(victoryGroup->buttonIsChecked(i)) + { + if(i < 3) + settings.victoryType = SETTINGS_VICTORY_TURNS; + else + { + settings.victoryType = SETTINGS_VICTORY_POINTS; + i -= 3; + } + if(i == 0) settings.duration = SETTINGS_DUR_SHORT; + else if(i == 1) settings.duration = SETTINGS_DUR_MEDIUM; + else if(i == 2) settings.duration = SETTINGS_DUR_LONG; + break; + } + } } @@ -2550,13 +2631,14 @@ if ( gameDataContainer.settings ) { sSettings *settings = gameDataContainer.settings; + text += lngPack.i18n ( "Text~Comp~GameEndsAt" ) + " " + settings->getVictoryConditionString() + "\n"; text += lngPack.i18n ( "Text~Title~Metal" ) + ": " + settings->getResValString ( settings->metal ) + "\n"; text += lngPack.i18n ( "Text~Title~Oil" ) + ": " + settings->getResValString ( settings->oil ) + "\n"; text += lngPack.i18n ( "Text~Title~Gold" ) + ": " + settings->getResValString ( settings->gold ) + "\n"; text += lngPack.i18n ( "Text~Title~Resource_Density" ) + ": " + settings->getResFreqString() + "\n"; text += lngPack.i18n ( "Text~Title~Credits" ) + ": " + iToStr( settings->credits ) + "\n"; text += lngPack.i18n ( "Text~Title~BridgeHead" ) + ": " + ( settings->bridgeHead == SETTING_BRIDGEHEAD_DEFINITE ? lngPack.i18n ( "Text~Option~Definite" ) : lngPack.i18n ( "Text~Option~Mobile" ) ) + "\n"; - text += lngPack.i18n ( "Text~Title~Alien_Tech" ) + ": " + ( settings->alienTech == SETTING_ALIENTECH_ON ? lngPack.i18n ( "Text~Option~On" ) : lngPack.i18n ( "Text~Option~Off" ) ) + "\n"; + //text += lngPack.i18n ( "Text~Title~Alien_Tech" ) + ": " + ( settings->alienTech == SETTING_ALIENTECH_ON ? lngPack.i18n ( "Text~Option~On" ) : lngPack.i18n ( "Text~Option~Off" ) ) + "\n"; text += string ("Clans") + ": " + ( settings->clans == SETTING_CLANS_ON ? lngPack.i18n ( "Text~Option~On" ) : lngPack.i18n ( "Text~Option~Off" ) ) + "\n"; text += lngPack.i18n ( "Text~Title~Game_Type" ) + ": " + ( settings->gameType == SETTINGS_GAMETYPE_TURNS ? lngPack.i18n ( "Text~Option~Type_Turns" ) : lngPack.i18n ( "Text~Option~Type_Simu" ) ) + "\n"; } @@ -3276,11 +3358,13 @@ if ( !gameDataContainer.settings ) gameDataContainer.settings = new sSettings; sSettings *settings = gameDataContainer.settings; + settings->duration = (eSettingsDuration)message->popInt16(); + settings->victoryType = (eSettingsVictoryType)message->popChar(); settings->metal = (eSettingResourceValue)message->popChar(); settings->oil = (eSettingResourceValue)message->popChar(); settings->gold = (eSettingResourceValue)message->popChar(); settings->resFrequency = (eSettingResFrequency)message->popChar(); - settings->credits = (eSettingsCredits)message->popInt16(); + settings->credits = (eSettingsCredits)message->popInt16(); settings->bridgeHead = (eSettingsBridgeHead)message->popChar(); settings->alienTech = (eSettingsAlienTech)message->popChar(); settings->clans = (eSettingsClans)message->popChar(); Index: trunk/serverevents.cpp =================================================================== --- trunk/serverevents.cpp (revision 2482) +++ trunk/serverevents.cpp (working copy) @@ -257,6 +257,7 @@ message->pushInt16( Building->data.buildCosts ); // Current state of the unit + message->pushInt16 ( Building->points ); message->pushBool ( Building->bSentryStatus ); message->pushBool ( Building->IsWorking ); message->pushInt16 ( Building->researchArea ); @@ -446,6 +447,62 @@ } //------------------------------------------------------------------------------------- +void sendScore( cPlayer *Subject, int turn, cPlayer *Receiver) +{ + if(!Receiver) + for ( unsigned int n = 0; n < Server->PlayerList->Size(); n++ ) + sendScore(Subject, turn, (*Server->PlayerList)[n]); + else + { + cNetMessage *msg = new cNetMessage( GAME_EV_SCORE ); + msg->pushInt16( Subject->getScore(turn)); + msg->pushInt16( turn ); + msg->pushInt16( Subject->Nr ); + + Server->sendNetMessage(msg, Receiver->Nr); + } +} + +void sendUnitScore(cBuilding *b) +{ + cNetMessage *msg = new cNetMessage( GAME_EV_UNIT_SCORE ); + msg->pushInt16(b->points); + msg->pushInt16(b->iID); + Server->sendNetMessage(msg, b->owner->Nr); +} + +void sendNumEcos(cPlayer *Subject, cPlayer *Receiver) +{ + Subject->CountEcoSpheres(); + + if(!Receiver) + for ( unsigned int n = 0; n < Server->PlayerList->Size(); n++ ) + sendNumEcos(Subject, (*Server->PlayerList)[n]); + else + { + cNetMessage *msg = new cNetMessage( GAME_EV_NUM_ECOS ); + msg->pushInt16( Subject->numEcos ); + msg->pushInt16( Subject->Nr ); + + Server->sendNetMessage(msg, Receiver->Nr); + } +} + +void sendVictoryConditions(int turnLimit, int scoreLimit, cPlayer *Receiver) +{ + if(!Receiver) + for ( unsigned int n = 0; n < Server->PlayerList->Size(); n++ ) + sendVictoryConditions(turnLimit, scoreLimit, (*Server->PlayerList)[n]); + else + { + cNetMessage *msg = new cNetMessage( GAME_EV_VICTORY_CONDITIONS ); + msg->pushInt16( turnLimit ); + msg->pushInt16( scoreLimit ); + Server->sendNetMessage(msg, Receiver->Nr); + } +} + +//------------------------------------------------------------------------------------- void sendBuildAnswer( bool bOK, cVehicle* vehicle ) { //message for the owner Index: trunk/menuitems.cpp =================================================================== --- trunk/menuitems.cpp (revision 2482) +++ trunk/menuitems.cpp (working copy) @@ -16,11 +16,77 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ + +#include +#include +#include #include "menuitems.h" #include "menus.h" #include "settings.h" #include "client.h" +namespace +{ + std::string plural(int n, const std::string &sing, const std::string &plu) + { + std::stringstream ss; + ss << n << " "; + ss << lngPack.i18n(n == 1 ? sing : plu); + return ss.str(); + } + + Uint32 getPlayerColour(cPlayer *p) + { + return ((Uint32*)(p->color->pixels))[0]; + } + + void plot(SDL_Surface *s, int x, int y, Uint32 colour) + { + SDL_Rect rect = {x, y, 1, 1}; + SDL_FillRect(s, &rect, colour); + } + + void drawLine(SDL_Surface *s, int x0, int y0, int x1, int y1, Uint32 colour) + { + bool steep = abs(y1 - y0) > abs(x1 - x0); + if(steep) { + std::swap(x0, y0); + std::swap(x1, y1); + } + if(x0 > x1) { + std::swap(x0, x1); + std::swap(y0, y1); + } + + int dx = x1 - x0; + int dy = abs(y1 - y0); + int er = dx / 2; + int ys = y0 < y1 ? 1 : -1; + int y = y0; + + for(int x=x0; xgetTurn(); + + if(turn <= now) + return p->getScore(turn); + else + return p->getScore(now) + p->numEcos * (turn - now); + } +} + cMenuItem::cMenuItem ( int x, int y ) { position.x = x; @@ -1653,8 +1719,22 @@ } // additional values - if ( ( data->storeResType != sUnitData::STORE_RES_NONE || data->storageUnitsMax > 0 ) && unitOwner == owner ) + if(data->canScore) { + int score = building->points; + int tot = building->owner->getScore(Client->getTurn()); + int lim = 400; + + cUnitDataSymbolHandler::drawNumber ( position.x+23, position.y+18, score, score ); + font->showText ( position.x+47, position.y+18, lngPack.i18n ( "Text~Hud~Score" ), FONT_LATIN_SMALL_WHITE, buffer ); + cUnitDataSymbolHandler::drawSymbols ( cUnitDataSymbolHandler::MENU_SYMBOLS_HUMAN, position.x+80, position.y+18, 70, false, score, score); + + cUnitDataSymbolHandler::drawNumber ( position.x+23, position.y+30, tot, lim ); + font->showText ( position.x+47, position.y+30, lngPack.i18n ( "Text~Hud~Total" ), FONT_LATIN_SMALL_WHITE, buffer ); + cUnitDataSymbolHandler::drawSymbols ( cUnitDataSymbolHandler::MENU_SYMBOLS_HUMAN, position.x+80, position.y+28, 70, false, tot, lim); + } + else if ( ( data->storeResType != sUnitData::STORE_RES_NONE || data->storageUnitsMax > 0 ) && unitOwner == owner ) + { font->showText ( position.x+47, position.y+18, lngPack.i18n ( "Text~Hud~Cargo" ), FONT_LATIN_SMALL_WHITE, buffer ); if ( data->storeResType > 0 ) @@ -3353,9 +3433,177 @@ void cMenuReportsScreen::drawScoreScreen() { - font->showText ( position.x+17, position.y+30, lngPack.i18n( "Text~Error_Messages~INFO_Not_Implemented" ) ); + int turnLimit, scoreLimit; + Client->getVictoryConditions(&turnLimit, &scoreLimit); + { + std::stringstream ss; + if(turnLimit) + { + ss << lngPack.i18n("Text~Comp~GameEndsAt") << " " << + plural(turnLimit, "Text~Comp~Turn", "Text~Comp~Turns"); + } + else if(scoreLimit) + { + ss << lngPack.i18n("Text~Comp~GameEndsAt") << " " << + plural(scoreLimit, "Text~Comp~Point", "Text~Comp~Points"); + } + else + ss << lngPack.i18n("Text~Comp~NoLimit"); + + font->showText(position.x+25, position.y+20, ss.str()); + } + + for (unsigned n=0, y=36; n < Client->PlayerList->Size(); n++, y+=16) + { + cPlayer *p = (*Client->PlayerList)[n]; + int score = p->getScore(Client->getTurn()); + int ecos = p->numEcos; + + SDL_Rect r = {position.x + 24, position.y + y + 3, 8, 8}; + SDL_FillRect(buffer, &r, getPlayerColour(p)); + + std::stringstream ss; + ss << p->name << ": " + << plural(score, "Text~Comp~Point", "Text~Comp~Points") << ", " + << plural(ecos, "Text~Comp~EcoSphere", "Text~Comp~EcoSpheres"); + + font->showText(position.x+42, position.y+y, ss.str()); + } + + drawScoreGraph(); } +void cMenuReportsScreen::drawScoreGraph() +{ + const Uint32 axis_colour = SDL_MapRGB(buffer->format, 164, 164, 164); + const Uint32 limit_colour = SDL_MapRGB(buffer->format, 128, 128, 128); + + const int px = position.x; + const int py = position.y; + + const int w = 400; + const int h = 300; + + const int x0 = px + 40, x1 = x0 + w; + const int y0 = py + 130, y1 = y0 + h; + + /* + Calculate time axis + */ + const int pix_per_turn = 5; + + const int now = Client->getTurn(); + + const int num_turns = w / pix_per_turn; + int max_turns = now + 10; + int min_turns = max_turns - num_turns; + + if(min_turns < 1) + { + const int over = 1 - min_turns; + min_turns += over; + max_turns += over; + } + + const int now_x = x0 + (now - min_turns) * pix_per_turn; + + /* + Calculate points axis + */ + int highest_score = 0; + int lowest_score = 0x7FFFFFFF; + for(unsigned n=0; n < Client->PlayerList->Size(); n++) + { + for(int turn = min_turns; turn < max_turns; turn++) + { + cPlayer *p = (*Client->PlayerList)[n]; + int score = extrapolateScore(p, turn); + if(score > highest_score) + highest_score = score; + if(score < lowest_score) + lowest_score = score; + } + } + + const int max_points = highest_score; + const int min_points = lowest_score; + const int num_points = max_points - min_points; + + const int max_pix_per_point = 5; + int pix_per_point; + if(num_points) { + pix_per_point = h / num_points; + if(pix_per_point > max_pix_per_point) + pix_per_point = max_pix_per_point; + } + else + pix_per_point = max_pix_per_point; + + /* + Draw Limits + */ + drawLine(buffer, now_x, y0, now_x, y1, limit_colour); + + int turn_lim, points_lim; + Client->getVictoryConditions(&turn_lim, &points_lim); + + if(turn_lim && turn_lim > min_turns && turn_lim < max_turns) + { + int x = x0 + (turn_lim - min_turns) * pix_per_turn; + + drawLine(buffer, x, y0, x, y1, limit_colour); + font->showTextCentered(x, y1 + 8, iToStr(turn_lim), FONT_LATIN_SMALL_WHITE); + } + if(points_lim && points_lim > min_points && points_lim < max_points) + { + int y = y1 - (points_lim - min_points) * pix_per_point; + + drawLine(buffer, x0, y, x1, y, limit_colour); + font->showText(x0 - 16, y - 3, iToStr(points_lim), FONT_LATIN_SMALL_WHITE); + } + + /* + Draw Labels + */ + font->showTextCentered(x0, y1 + 8, iToStr(min_turns), FONT_LATIN_SMALL_WHITE); + font->showTextCentered(now_x, y1 + 8, iToStr(now), FONT_LATIN_SMALL_WHITE); + font->showTextCentered(x1, y1 + 8, iToStr(max_turns), FONT_LATIN_SMALL_WHITE); + + font->showText(x0 - 16, y1 - 3, iToStr(min_points), FONT_LATIN_SMALL_WHITE); + font->showText(x0 - 16, y0 - 3, iToStr(max_points), FONT_LATIN_SMALL_WHITE); + + /* + Draw Score Lines + */ + for(unsigned n=0, y=40; n < Client->PlayerList->Size(); n++, y+=25) + { + cPlayer *p = (*Client->PlayerList)[n]; + Uint32 player_colour = getPlayerColour(p); + + int lx, ly; + + for(int turn = min_turns; turn < max_turns; turn++) + { + int points = extrapolateScore(p, turn); + + int x = x0 + pix_per_turn * (turn - min_turns); + int y = y1 - pix_per_point * (points - min_points); + + if(turn != min_turns) + drawLine(buffer, lx, ly, x, y, player_colour); + + lx = x; + ly = y; + } + } + + /* + Draw Axes + */ + drawLine(buffer, x0, y0, x0, y1, axis_colour); + drawLine(buffer, x0, y1, x1, y1, axis_colour); +} + void cMenuReportsScreen::drawReportsScreen() { SDL_Rect textDest = { position.x+54, position.y+25, 410, 30 }; Index: trunk/client.cpp =================================================================== --- trunk/client.cpp (revision 2482) +++ trunk/client.cpp (working copy) @@ -130,6 +130,7 @@ bAlienTech = false; bWaitForOthers = false; iTurnTime = 0; + scoreLimit = turnLimit = 0; } cClient::~cClient() @@ -947,6 +948,7 @@ Building->researchArea = message->popInt16(); Building->IsWorking = message->popBool(); Building->bSentryStatus = message->popBool(); + Building->points = message->popInt16(); if ( Building->Disabled > 0 != bWasDisabled ) Building->owner->DoScan(); Data = &Building->data; @@ -1994,6 +1996,35 @@ ActivePlayer->savedReportsList.Add ( savedReport ); } break; + case GAME_EV_SCORE: + { + int pn = message->popInt16(); + int turn = message->popInt16(); + int n = message->popInt16(); + + getPlayerFromNumber(pn)->setScore(n, turn); + } + break; + case GAME_EV_NUM_ECOS: + { + int pn = message->popInt16(); + int n = message->popInt16(); + + getPlayerFromNumber(pn)->numEcos = n; + } + break; + case GAME_EV_UNIT_SCORE: + { + cBuilding *b = getBuildingFromID(message->popInt16()); + b->points = message->popInt16(); + } + break; + case GAME_EV_VICTORY_CONDITIONS: + { + scoreLimit = message->popInt16(); + turnLimit = message->popInt16(); + } + break; default: Log.write("Client: Can not handle message type " + message->getTypeAsString(), cLog::eLOG_TYPE_NET_ERROR); break; @@ -2463,3 +2494,14 @@ } } } + +int cClient::getTurn() const +{ + return iTurn; +} + +void cClient::getVictoryConditions(int *turnLimit, int *scoreLimit) const +{ + *turnLimit = this->turnLimit; + *scoreLimit = this->scoreLimit; +} Index: trunk/buildings.h =================================================================== --- trunk/buildings.h (revision 2482) +++ trunk/buildings.h (working copy) @@ -172,6 +172,7 @@ bool hasBeenAttacked; cList passiveEndMoveActions; int selMenuNr; + int points; // accumulated eco-sphere points /** * draws the building to the screen. It takes the main image from the drawing cache, or calls the cBuilding::render() function. Index: trunk/player.cpp =================================================================== --- trunk/player.cpp (revision 2482) +++ trunk/player.cpp (working copy) @@ -33,7 +33,8 @@ color(Color), Nr(nr), base(this), - clan(-1) + clan(-1), + numEcos(0) { // copy the vehicle stats VehicleData = new sUnitData[UnitsData.getNrVehicles ()]; @@ -60,7 +61,7 @@ researchCentersWorkingOnArea[i] = 0; Credits=0; reportResearchFinished = false; - + this->iSocketNum = iSocketNum; isDefeated = false; bFinishedTurn = false; @@ -77,6 +78,8 @@ Nr = Player.Nr; iSocketNum = Player.iSocketNum; clan = Player.clan; + pointsHistory = Player.pointsHistory; + numEcos = Player.numEcos; // copy vehicle and building datas VehicleData = new sUnitData[UnitsData.getNrVehicles ()]; @@ -648,6 +651,59 @@ reportResearchFinished = researchFinished; } +void cPlayer::accumulateScore() +{ + const int now = Server->getTurn(); + int deltaScore = 0; + + for(cBuilding *bp = BuildingList; bp; bp = bp->next) + { + if ( bp->typ->data.canScore && bp->IsWorking ) + { + bp->points ++; + deltaScore ++; + + sendUnitScore(bp); + } + } + setScore(getScore(now) + deltaScore, now); + sendScore(this, now); +} + +void cPlayer::CountEcoSpheres() +{ + numEcos = 0; + + for(cBuilding *bp = BuildingList; bp; bp = bp->next) + { + if ( bp->typ->data.canScore && bp->IsWorking ) + numEcos ++; + } +} + +void cPlayer::setScore(int s, int turn) +{ + int t = turn ? turn : (Client ? Client->iTurn : 1); + + if(pointsHistory.size() < t) + pointsHistory.resize(t); + pointsHistory[t - 1] = s; +} + +int cPlayer::getScore(int turn) const +{ + int t = turn; + + if(pointsHistory.size() < t) + { + int score = pointsHistory.empty() ? + 0 : pointsHistory[pointsHistory.size() - 1]; + pointsHistory.resize(t); + pointsHistory[t - 1] = score; + } + return pointsHistory[t - 1]; +} + //-------------------------------------------------------------- void cPlayer::upgradeUnitTypes (cList& areasReachingNextLevel, cList& resultUpgradedUnitDatas) { Index: trunk/server.cpp =================================================================== --- trunk/server.cpp (revision 2482) +++ trunk/server.cpp (working copy) @@ -16,6 +16,8 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include +#include #include "server.h" #include "client.h" #include "events.h" @@ -44,8 +46,12 @@ } //------------------------------------------------------------------------------------- -cServer::cServer(cMap* const map, cList* const PlayerList, eGameTypes const gameType, bool const bPlayTurns) +cServer::cServer(cMap* const map, cList* const PlayerList, eGameTypes const gameType, bool const bPlayTurns, int turnLimit, int scoreLimit) { + assert(!(turnLimit && scoreLimit)); + + this->turnLimit = turnLimit; + this->scoreLimit = scoreLimit; bDebugCheckPos = false; Map = map; this->PlayerList = PlayerList; @@ -186,6 +192,9 @@ //------------------------------------------------------------------------------------- void cServer::run() { + for(unsigned n = 0; n < PlayerList->Size(); n++) + sendVictoryConditions(turnLimit, scoreLimit, (*PlayerList)[n]); + while ( !bExit ) { SDL_Event* event = pollEvent(); @@ -2091,6 +2100,16 @@ if ( AJobs[i]->building == Building ) AJobs[i]->building = NULL; } } + + // lose eco points + if(Building->points) + { + Building->owner->setScore( + Building->owner->getScore(iTurn) - Building->points, + iTurn + ); + sendScore(Building->owner, iTurn); + } Map->deleteBuilding( Building ); @@ -2581,6 +2600,12 @@ // do research: for (unsigned int i = 0; i < PlayerList->Size(); i++) (*PlayerList)[i]->doResearch(); + + // eco-spheres: + for (unsigned int i = 0; i < PlayerList->Size(); i++) + { + (*PlayerList)[i]->accumulateScore(); + } // Gun'em down: for ( unsigned int i = 0; i < PlayerList->Size(); i++ ) @@ -2614,11 +2639,30 @@ //------------------------------------------------------------------------------------- void cServer::checkDefeats () { + std::set winners, losers; + int best_score = 0; + for ( unsigned int i = 0; i < PlayerList->Size(); i++ ) { cPlayer *Player = (*PlayerList)[i]; if ( !Player->isDefeated ) { + int score = Player->getScore(iTurn); + if( + (scoreLimit && score >= scoreLimit) || + (turnLimit && iTurn >= turnLimit) + ){ + if(score >= best_score) + { + if(score > best_score) + { + winners.clear(); + best_score = score; + } + winners.insert(Player); + } + } + cBuilding *Building = Player->BuildingList; cVehicle *Vehicle = Player->VehicleList; while ( Vehicle ) @@ -2634,17 +2678,46 @@ } if ( Building != NULL ) continue; - Player->isDefeated = true; - sendDefeated ( Player ); + losers.insert(Player); + } + } + + // If some players have won, anyone who hasn't won has lost. + if(!winners.empty()) + for(unsigned int i = 0; i < PlayerList->Size(); i++) + { + cPlayer *Player = (*PlayerList)[i]; + + if(winners.find(Player) == winners.end()) + losers.insert(Player); + } + + // Defeat all players who have lost. + for(std::set::iterator i = losers.begin(); i != losers.end(); ++i) + { + cPlayer *Player = *i; + + Player->isDefeated = true; + sendDefeated ( Player ); - if ( openMapDefeat && Player->iSocketNum != -1 ) - { - memset ( Player->ScanMap, 1, Map->size*Map->size ); - checkPlayerUnits(); - sendNoFog ( Player->Nr ); - } + if ( openMapDefeat && Player->iSocketNum != -1 ) + { + memset ( Player->ScanMap, 1, Map->size*Map->size ); + checkPlayerUnits(); + sendNoFog ( Player->Nr ); } } + + /* + Handle the case where there is more than one winner. Original MAX calls + a draw and displays the results screen. For now we will have sudden + death, i.e. first player to get ahead in score wins. + */ + if(winners.size() > 1) + { + for ( unsigned int i = 0; i < PlayerList->Size(); i++ ) + sendChatMessageToClient("Text~Comp~SuddenDeath", SERVER_INFO_MESSAGE, i); + } } //------------------------------------------------------------------------------------- @@ -3244,6 +3317,17 @@ // send research sendResearchLevel( &(Player->researchLevel), Player->Nr ); sendRefreshResearchCount (Player->Nr); + + // send all players' score histories & eco-counts + for(int t=1; t<=iTurn; t++) + for(unsigned int i = 0; i < PlayerList->Size(); i++ ) + { + cPlayer *subj = (*PlayerList)[i]; + sendScore(subj, t, Player); + sendNumEcos(subj, Player); + } + + sendVictoryConditions(turnLimit, scoreLimit, Player); Log.write(" Server: ============================= end resync ==========================", cLog::eLOG_TYPE_NET_DEBUG); } @@ -3477,3 +3561,9 @@ savingIndex = saveNum; sendRequestSaveInfo ( savingID ); } + +int cServer::getTurn() const +{ + return iTurn; +} + Index: trunk/loaddata.cpp =================================================================== --- trunk/loaddata.cpp (revision 2482) +++ trunk/loaddata.cpp (working copy) @@ -2832,6 +2832,7 @@ Data->doesSelfRepair = getXMLNodeBool ( unitDataXml, "Unit", "Abilities", "Does_Self_Repair" ); Data->convertsGold = getXMLNodeInt ( unitDataXml, "Unit", "Abilities", "Converts_Gold" ); Data->canSelfDestroy = getXMLNodeBool ( unitDataXml, "Unit", "Abilities", "Can_Self_Destroy" ); + Data->canScore = getXMLNodeBool ( unitDataXml, "Unit", "Abilities", "Can_Score" ); Data->canMineMaxRes = getXMLNodeInt ( unitDataXml, "Unit", "Abilities", "Can_Mine_Max_Resource" ); Index: trunk/menuitems.h =================================================================== --- trunk/menuitems.h (revision 2482) +++ trunk/menuitems.h (working copy) @@ -1176,6 +1176,7 @@ void drawUnitsScreen(); void drawDisadvantagesScreen(); void drawScoreScreen(); + void drawScoreGraph(); void drawReportsScreen(); public: Index: trunk/client.h =================================================================== --- trunk/client.h (revision 2482) +++ trunk/client.h (working copy) @@ -104,8 +104,6 @@ friend class cVehicle; friend class cUnit; - /** List with all players */ - cList *PlayerList; /** list with buildings without owner, e. g. rubble fields */ cBuilding* neutralBuildings; /** ID of the timer */ @@ -127,6 +125,8 @@ int iTurnTime; /** Ticks when the TurnTime has been started */ unsigned int iStartTurnTime; + /** this client's copy of the victory conditions **/ + int turnLimit, scoreLimit; /** * handles the game relevant actions (for example moving the current position of a rocket) @@ -209,6 +209,9 @@ /** shows if the player has to wait for other players */ bool bWaitForOthers; bool waitReconnect; + + /** List with all players */ + cList *PlayerList; /** * handles the timers timer50ms, timer100ms and timer400ms @@ -302,7 +305,11 @@ void destroyUnit( cVehicle* vehicle ); void destroyUnit( cBuilding* building ); - void checkVehiclePositions( cNetMessage* message ); + void checkVehiclePositions(cNetMessage* message); + void getVictoryConditions(int *turnLimit, int *scoreLimit) const; + int getTurn() const; + + } EX *Client; #endif Index: trunk/main.cpp =================================================================== --- trunk/main.cpp (revision 2482) +++ trunk/main.cpp (working copy) @@ -842,6 +842,7 @@ doesSelfRepair = false; convertsGold = 0; canSelfDestroy = false; + canScore = false; canMineMaxRes = 0; needsMetal = 0; Index: trunk/savegame.cpp =================================================================== --- trunk/savegame.cpp (revision 2482) +++ trunk/savegame.cpp (working copy) @@ -239,6 +239,10 @@ Server->bPlayTurns = true; element->Attribute ( "activeplayer", &Server->iActiveTurnPlayerNr ); } + + TiXmlElement *e; + if(e = gameInfoNode->FirstChildElement("TurnLimit")) e->Attribute("num", &Server->turnLimit); + if(e = gameInfoNode->FirstChildElement("ScoreLimit")) e->Attribute("num", &Server->scoreLimit); } //-------------------------------------------------------------------------- @@ -289,6 +293,7 @@ cPlayer *cSavegame::loadPlayer( TiXmlElement *playerNode, cMap *map ) { int number, color; + TiXmlElement *e; string name = playerNode->FirstChildElement( "Name" )->Attribute ( "string" ); playerNode->FirstChildElement( "Number" )->Attribute ( "num", &number ); @@ -298,6 +303,24 @@ Player->InitMaps ( map->size, map ); playerNode->FirstChildElement( "Credits" )->Attribute ( "num", &Player->Credits ); + + if(e = playerNode->FirstChildElement("ScoreHistory")) + { + TiXmlElement *s = e->FirstChildElement("Score"); + int num=0, i=0; + while(s) + { + s->Attribute("num", &num); + Player->pointsHistory.resize(i + 1); + Player->pointsHistory[i] = num; + i++; + s = s->NextSiblingElement("Score"); + } + // add current turn + Player->pointsHistory.push_back(num); + } + else + number = 0; int clan = -1; if (TiXmlElement* const element = playerNode->FirstChildElement("Clan")) element->Attribute("num", &clan); @@ -688,6 +711,7 @@ if ( unitNode->FirstChildElement( "IsWorking" ) ) building->IsWorking = true; if ( unitNode->FirstChildElement( "ResearchArea" ) ) unitNode->FirstChildElement( "ResearchArea" )->Attribute( "area", &(building->researchArea) ); + if ( unitNode->FirstChildElement( "Score" ) ) unitNode->FirstChildElement( "Score" )->Attribute( "num", &(building->points) ); if ( unitNode->FirstChildElement( "OnSentry" ) ) { if ( !building->bSentryStatus ) @@ -1193,6 +1217,9 @@ addAttributeElement ( gemeinfoNode, "Turn", "num", iToStr ( Server->iTurn ) ); if ( Server->bHotSeat ) addAttributeElement ( gemeinfoNode, "Hotseat", "activeplayer", iToStr ( Server->iHotSeatPlayer ) ); if ( Server->bPlayTurns ) addAttributeElement ( gemeinfoNode, "PlayTurns", "activeplayer", iToStr ( Server->iActiveTurnPlayerNr ) ); + + addAttributeElement ( gemeinfoNode, "TurnLimit", "num", iToStr ( Server->turnLimit ) ); + addAttributeElement ( gemeinfoNode, "ScoreLimit", "num", iToStr ( Server->scoreLimit ) ); } //-------------------------------------------------------------------------- @@ -1223,7 +1250,15 @@ addAttributeElement ( playerNode, "Color", "num", iToStr ( GetColorNr ( Player->color ) ) ); addAttributeElement ( playerNode, "Number", "num", iToStr ( Player->Nr ) ); addAttributeElement ( playerNode, "ResourceMap", "data", convertScanMapToString ( Player->ResourceMap, Server->Map->size*Server->Map->size ) ); - + + // player score + TiXmlElement *scoreNode = addMainElement(playerNode, "ScoreHistory"); + for(int i=0; ipointsHistory.size(); i++) + { + TiXmlElement *e = addMainElement(scoreNode, "Score"); + e->SetAttribute("num", iToStr(Player->pointsHistory[i]).c_str()); + } + // write data of upgraded units TiXmlElement *upgradesNode = addMainElement ( playerNode, "Upgrades" ); int upgrades = 0; @@ -1437,6 +1472,10 @@ TiXmlElement *researchNode = addMainElement ( unitNode, "ResearchArea" ); researchNode->SetAttribute ( "area", iToStr(Building->researchArea).c_str() ); } + if ( Building->data.canScore ) + { + addAttributeElement( unitNode, "Score", "num", iToStr(Building->points)); + } if ( Building->bSentryStatus ) addMainElement ( unitNode, "OnSentry" ); if ( Building->hasBeenAttacked ) addMainElement ( unitNode, "HasBeenAttacked" ); Index: trunk/menuevents.cpp =================================================================== --- trunk/menuevents.cpp (revision 2482) +++ trunk/menuevents.cpp (working copy) @@ -78,6 +78,8 @@ message->pushChar ( gameData->settings->gold ); message->pushChar ( gameData->settings->oil ); message->pushChar ( gameData->settings->metal ); + message->pushChar ( gameData->settings->victoryType ); + message->pushInt16 ( gameData->settings->duration ); } message->pushBool ( gameData->settings != NULL ); Index: trunk/player.h =================================================================== --- trunk/player.h (revision 2482) +++ trunk/player.h (working copy) @@ -18,6 +18,7 @@ ***************************************************************************/ #ifndef playerH #define playerH +#include #include "defines.h" #include "main.h" #include @@ -63,7 +64,9 @@ int colorNr; }; +typedef std::vector PointsHistory; + // Die Player-Klasse ///////////////////////////////////////////////////////// class cPlayer{ @@ -97,6 +100,7 @@ int researchCentersWorkingOnArea[cResearch::kNrResearchAreas]; ///< counts the number of research centers that are currently working on each area int ResearchCount; ///< number of working research centers int Credits; // Anzahl der erworbenen Credits. + mutable PointsHistory pointsHistory; // history of player's total score (from eco-spheres) for graph sHudStateContainer *savedHud; cList ReportVehicles; // Reportlisten. cList ReportBuildings; // Reportlisten. @@ -107,7 +111,8 @@ // if MAX_CLIENTS its the lokal connected player; -1 for unknown bool bFinishedTurn; //true when player send his turn end bool isDefeated; // true if the player has been defeated - + int numEcos; // number of ecospheres. call CountEcoSpheres on server to update. + void InitMaps(int MapSizeX, cMap *map = NULL ); // TODO: remove ' = NULL' void DoScan(); cVehicle *GetNextVehicle(); @@ -121,6 +126,7 @@ void startAResearch (int researchArea); void stopAResearch (int researchArea); void doResearch (); ///< proceed with the research at turn end + void accumulateScore(); // at turn end void upgradeUnitTypes (cList& areasReachingNextLevel, cList& resultUpgradedUnitDatas); void refreshResearchCentersWorkingOnArea(); void AddLock(cBuilding *b); @@ -131,6 +137,10 @@ bool InLockList(cVehicle *v); void ToggelLock(cMapField *OverUnitField); void DrawLockList(); + void CountEcoSpheres(); + int getScore(int turn) const; + void setScore(int score, int turn); + /** * draws a circle on the map for the fog *@author alzi alias DoctorDeath Index: trunk/clientevents.h =================================================================== --- trunk/clientevents.h (revision 2482) +++ trunk/clientevents.h (working copy) @@ -85,6 +85,10 @@ GAME_EV_COMMANDO_ANSWER, // information about the result of a commando action GAME_EV_REQ_SAVE_INFO, // request the hud state and the saved reports from a client GAME_EV_SAVED_REPORT, // sends saved reports to a client + GAME_EV_SCORE, // sends a player's score to a client + GAME_EV_NUM_ECOS, // sends a player's ecosphere count to a client + GAME_EV_UNIT_SCORE, // sends a unit's score to its owner + GAME_EV_VICTORY_CONDITIONS, // the game's victory conditions DEBUG_CHECK_VEHICLE_POSITIONS // sends all vehicle positions to the clients to find async vehicles }; Index: trunk/server.h =================================================================== --- trunk/server.h (revision 2482) +++ trunk/server.h (working copy) @@ -69,13 +69,16 @@ friend class cSavegame; public: /** - * initialises the server class + * initialises the server class. turnLimit and scoreLimit should not + both be set. If both are zero, it's last man standing. *@author alzi alias DoctorDeath *@param map The Map for the game *@param PlayerList The list with all players *@param iGameType The type of the game. Can be GAME_TYPE_SINGLE, GAME_TYPE_HOTSEAT or GAME_TYPE_TCPIP + *@param turnLimit Game ends after this many turns + *@param scoreLimit First player to this many points wins */ - cServer(cMap* map, cList* PlayerList, eGameTypes gameType, bool bPlayTurns); + cServer(cMap* map, cList* PlayerList, eGameTypes gameType, bool bPlayTurns, int turnLimit=0, int scoreLimit=0); void setDeadline(int iDeadline); ~cServer(); @@ -133,6 +136,9 @@ int savingID; /** the index of the saveslot where additional save info should be added */ int savingIndex; + + /** victory conditions. One or both must be zero. **/ + int turnLimit, scoreLimit; /** * returns a pointer to the next event of the eventqueue. If the queue is empty it will return NULL. @@ -389,6 +395,8 @@ void sideStepStealthUnit( int PosX, int PosY, sUnitData& vehicleData, cPlayer* vehicleOwner, int bigOffset = -1 ); void makeAdditionalSaveRequest ( int saveNum ); + + int getTurn() const; } EX *Server; Index: trunk/main.h =================================================================== --- trunk/main.h (revision 2482) +++ trunk/main.h (working copy) @@ -248,6 +248,7 @@ bool doesSelfRepair; int convertsGold; bool canSelfDestroy; + bool canScore; int canMineMaxRes; Index: trunk/menus.h =================================================================== --- trunk/menus.h (revision 2482) +++ trunk/menus.h (working copy) @@ -111,6 +111,20 @@ SETTINGS_GAMETYPE_TURNS }; +enum eSettingsVictoryType +{ + SETTINGS_VICTORY_TURNS, + SETTINGS_VICTORY_POINTS, + SETTINGS_VICTORY_ANNIHILATION +}; + +enum eSettingsDuration +{ + SETTINGS_DUR_SHORT = 100, + SETTINGS_DUR_MEDIUM = 200, + SETTINGS_DUR_LONG = 400 +}; + /** * A class that containes all settings for a new game. *@author alzi @@ -124,12 +138,16 @@ eSettingsAlienTech alienTech; eSettingsClans clans; eSettingsGameType gameType; + eSettingsVictoryType victoryType; + eSettingsDuration duration; sSettings() : metal(SETTING_RESVAL_NORMAL), oil(SETTING_RESVAL_NORMAL), gold(SETTING_RESVAL_NORMAL), resFrequency(SETTING_RESFREQ_NORMAL), credits(SETTING_CREDITS_NORMAL), - bridgeHead (SETTING_BRIDGEHEAD_DEFINITE), alienTech(SETTING_ALIENTECH_OFF), clans(SETTING_CLANS_ON), gameType(SETTINGS_GAMETYPE_SIMU) {} + bridgeHead (SETTING_BRIDGEHEAD_DEFINITE), alienTech(SETTING_ALIENTECH_OFF), clans(SETTING_CLANS_ON), gameType(SETTINGS_GAMETYPE_SIMU), victoryType(SETTINGS_VICTORY_POINTS), + duration(SETTINGS_DUR_MEDIUM) {} string getResValString ( eSettingResourceValue type ); string getResFreqString(); + string getVictoryConditionString(); }; @@ -417,6 +435,7 @@ cMenuLabel *clansLabel; cMenuLabel *resFrequencyLabel; cMenuLabel *gameTypeLabel; + cMenuLabel *victoryLabel; cMenuRadioGroup *metalGroup; cMenuRadioGroup *oilGroup; @@ -427,6 +446,7 @@ cMenuRadioGroup *clansGroup; cMenuRadioGroup *resFrequencyGroup; cMenuRadioGroup *gameTypeGroup; + cMenuRadioGroup *victoryGroup; void updateSettings(); public: Index: resinstaller/resinstaller.cpp =================================================================== --- resinstaller/resinstaller.cpp (revision 2482) +++ resinstaller/resinstaller.cpp (working copy) @@ -1559,7 +1559,15 @@ copyFileFromRes("S_DOCK", path + "shw.pcx"); copyFileFromRes("P_DOCK", path + "info.pcx"); copyImageFromFLC( sMAXPath + "DOCK.FLC", path + "video.pcx"); - + + //eco-sphere + path = sOutputPath + "buildings" + PATH_DELIMITER + "ecosphere" + PATH_DELIMITER; + copyFileFromRes_rpc("GREENHSE", path + "img.pcx"); + copyFileFromRes("S_GREENH", path + "shw.pcx"); + copyFileFromRes("P_GREENH", path + "info.pcx"); + copyImageFromFLC( sMAXPath + "GRNHOUSE.FLC", path + "video.pcx"); + copyFile( path + "effect_org.pcx", path + "effect.pcx"); + //energy big path = sOutputPath + "buildings" + PATH_DELIMITER + "energy_big" + PATH_DELIMITER; copyFileFromRes_rpc("POWERSTN", path + "img.pcx"); Index: game/buildings/buildings.xml =================================================================== --- game/buildings/buildings.xml (revision 2482) +++ game/buildings/buildings.xml (working copy) @@ -49,6 +49,7 @@ + Index: game/buildings/ecosphere/graphics.xml =================================================================== --- game/buildings/ecosphere/graphics.xml (revision 0) +++ game/buildings/ecosphere/graphics.xml (revision 0) @@ -0,0 +1,20 @@ + + +
+ + + + +
+ + + + + + + + + +
+ + Index: game/buildings/ecosphere/video.pcx =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: game\buildings\ecosphere\video.pcx ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: game/buildings/ecosphere/info.pcx =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: game\buildings\ecosphere\info.pcx ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: game/buildings/ecosphere/effect.pcx =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: game\buildings\ecosphere\effect.pcx ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: game/buildings/ecosphere/img.pcx =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: game\buildings\ecosphere\img.pcx ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: game/buildings/ecosphere/data.xml =================================================================== --- game/buildings/ecosphere/data.xml (revision 0) +++ game/buildings/ecosphere/data.xml (revision 0) @@ -0,0 +1,39 @@ + + +
+ + + + +
+ + Eco-sphere\n\nYour primary goal is the construction of eco-spheres. Each eco-sphere gives you one colony point per turn. + \n\nDefend your eco-spheres well, because if one is destroyed, you lose all the points it generated for you! + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Index: game/buildings/ecosphere/shw.pcx =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: game\buildings\ecosphere\shw.pcx ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: game/languages/lang_eng.xml =================================================================== --- game/languages/lang_eng.xml (revision 2482) +++ game/languages/lang_eng.xml (working copy) @@ -181,6 +181,7 @@ +