From 5279e2e9fce945e5f6e803663f42638bf8f2a790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=2E=20Urba=C5=84czyk?= Date: Fri, 29 Jan 2010 20:52:45 +0000 Subject: [PATCH] First part of support for victory & loss conditions. Implemented and tested are victory: Defeat hero Capture town Defeat monster Flag dwellings Flag mines Lose: Loss hero Time expire ** Some others may work but not has been tested yet. I've added a new page in VCMI Status spreadsheet with status of various victory/loss conditions. --- CGameInterface.h | 1 + client/CMT.cpp | 4 + client/CPlayerInterface.cpp | 22 +++- client/CPlayerInterface.h | 1 + client/CPreGame.cpp | 1 + client/Client.cpp | 12 ++- client/GUIClasses.cpp | 1 + client/NetPacksClient.cpp | 6 ++ client/VCMI_client.vcproj | 8 ++ global.h | 2 +- hch/CObjectHandler.cpp | 11 ++ hch/CObjectHandler.h | 8 +- lib/CGameState.cpp | 205 +++++++++++++++++++++++++++++++++++- lib/CGameState.h | 126 ++++++++++++---------- lib/Connection.cpp | 39 +++++-- lib/Connection.h | 24 +++-- lib/NetPacks.h | 20 ++++ lib/NetPacksLib.cpp | 8 ++ lib/RegisterTypes.cpp | 1 + lib/map.cpp | 53 ++++++++-- lib/map.h | 19 +++- server/CGameHandler.cpp | 171 ++++++++++++++++++++++++++++-- server/CGameHandler.h | 5 +- 23 files changed, 643 insertions(+), 105 deletions(-) diff --git a/CGameInterface.h b/CGameInterface.h index 6cbefbe0a..c0115f47f 100644 --- a/CGameInterface.h +++ b/CGameInterface.h @@ -103,6 +103,7 @@ public: virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged virtual void objectRemoved(const CGObjectInstance *obj){}; //eg. collected resource, picked artifact, beaten hero virtual void playerBlocked(int reason){}; //reason: 0 - upcoming battle + virtual void gameOver(ui8 player, bool victory){}; //player lost or won the game virtual void serialize(COSer &h, const int version){}; //saving virtual void serialize(CISer &h, const int version){}; //loading diff --git a/client/CMT.cpp b/client/CMT.cpp index 127a1e72f..500af094f 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -169,6 +169,7 @@ int _tmain(int argc, _TCHAR* argv[]) int main(int argc, char** argv) #endif { + //Set environment vars to make window centered. Sometimes work, sometimes not. :/ putenv("SDL_VIDEO_WINDOW_POS"); putenv("SDL_VIDEO_CENTERED=1"); @@ -504,6 +505,9 @@ static void listenForEvents() else if (ev->type == SDL_USEREVENT && ev->user.code == 2) //something want to quit to main menu { client->stop(); + delete client; + client = NULL; + delete ev; GH.curInt = CGP; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index cb0b40123..0439a07c9 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -746,7 +746,7 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector pom.push_back(std::pair >("IOKAY.DEF",0)); CInfoWindow * temp = new CInfoWindow(text,playerID,0,components,pom,false); - if(makingTurn && GH.listInt.size()) + if(/*makingTurn && */GH.listInt.size()) { CGI->soundh->playSound(static_cast(soundID)); showingDialog->set(true); @@ -1590,6 +1590,26 @@ void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &h std::stable_sort(CGI->mh->ttiles[details.end.x-1][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-1][details.end.y][details.end.z].objects.end(), ocmptwo_cgin); std::stable_sort(CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.end(), ocmptwo_cgin); } + +void CPlayerInterface::gameOver(ui8 player, bool victory ) +{ + if(player == playerID) + { + if(!victory) + showInfoDialog(CGI->generaltexth->allTexts[95]); +// else +// showInfoDialog("Placeholder message: you won!"); + + waitWhileDialog(); + + //return to main menu + SDL_Event event; + event.type = SDL_USEREVENT; + event.user.code = 2; + SDL_PushEvent(&event); + } +} + void SystemOptions::setMusicVolume( int newVolume ) { musicVolume = newVolume; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index dc1d5ca14..554e3113c 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -170,6 +170,7 @@ public: void centerView (int3 pos, int focusTime); void objectPropertyChanged(const SetObjectProperty * sop); void objectRemoved(const CGObjectInstance *obj); + void gameOver(ui8 player, bool victory); void serialize(COSer &h, const int version); //saving void serialize(CISer &h, const int version); //loading diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index 4289128c9..5cbac3de5 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -14,6 +14,7 @@ #include "../hch/CLodHandler.h" #include "../hch/CTownHandler.h" #include "../hch/CHeroHandler.h" +#include "../hch/CObjectHandler.h" #include #include "Graphics.h" //#include diff --git a/client/Client.cpp b/client/Client.cpp index ee3d7e2c1..79571742d 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -153,7 +153,17 @@ void CClient::run() handlePack(pack); pack = NULL; } - } HANDLE_EXCEPTION(tlog1 << "Lost connection to server, ending listening thread!\n"); + } + catch (const std::exception& e) + { + tlog3 << "Lost connection to server, ending listening thread!\n"; + tlog1 << e.what() << std::endl; + if(!terminate) //rethrow (-> boom!) only if closing connection was unexpected + { + tlog1 << "Something wrong, lost connection while game is still ongoing...\n"; + throw; + } + } } void CClient::stop() diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 4b84dc4cb..94411a796 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -628,6 +628,7 @@ void CGarrisonInt::deactivate() CInfoWindow::CInfoWindow(std::string text, int player, int charperline, const std::vector &comps, std::vector > > &Buttons, bool delComps) { + slider = NULL; ID = -1; this->delComps = delComps; for(int i=0;imh->printObject(obj); } +void PlayerEndsGame::applyCl( CClient *cl ) +{ + for(std::map::iterator i=cl->playerint.begin();i!=cl->playerint.end();i++) + i->second->gameOver(player, victory); +} + void RemoveObject::applyFirstCl( CClient *cl ) { const CGObjectInstance *o = cl->getObj(id); diff --git a/client/VCMI_client.vcproj b/client/VCMI_client.vcproj index e3b43c4a3..896c91932 100644 --- a/client/VCMI_client.vcproj +++ b/client/VCMI_client.vcproj @@ -325,6 +325,10 @@ RelativePath=".\CHeroWindow.cpp" > + + @@ -471,6 +475,10 @@ RelativePath=".\CHeroWindow.h" > + + diff --git a/global.h b/global.h index 184209eae..c16158583 100644 --- a/global.h +++ b/global.h @@ -20,7 +20,7 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte) #define THC #endif -#define NAME_VER ("VCMI 0.75") +#define NAME_VER ("VCMI 0.75b") extern std::string NAME; //full name extern std::string NAME_AFFIX; //client / server #define CONSOLE_LOGGING_LEVEL 5 diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index 57386a796..51bc30b70 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -1234,6 +1234,17 @@ void CGHeroInstance::recreateArtBonuses() } } +bool CGHeroInstance::hasArt( ui32 aid ) const +{ + if(vstd::contains(artifacts, aid)) + return true; + for(std::map::const_iterator i = artifWorn.begin(); i != artifWorn.end(); i++) + if(i->second == aid) + return true; + + return true; +} + void CGDwelling::initObj() { switch(ID) diff --git a/hch/CObjectHandler.h b/hch/CObjectHandler.h index 810081bf9..5536af79a 100644 --- a/hch/CObjectHandler.h +++ b/hch/CObjectHandler.h @@ -298,10 +298,12 @@ public: int getPrimSkillLevel(int id) const; ui8 getSecSkillLevel(const int & ID) const; //0 - no skill int maxMovePoints(bool onLand) const; + ui32 getArtAtPos(ui16 pos) const; //-1 - no artifact const CArtifact * getArt(int pos) const; si32 getArtPos(int aid) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned) - void giveArtifact (ui32 aid); + bool hasArt(ui32 aid) const; //checks if hero possess artifact of given id (either in backack or worn) + int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest double getHeroStrength() const; @@ -316,7 +318,9 @@ public: void initHero(); void initHero(int SUBID); void recreateArtBonuses(); + void giveArtifact (ui32 aid); void initHeroDefInfo(); + CGHeroInstance(); virtual ~CGHeroInstance(); @@ -605,7 +609,7 @@ public: template void serialize(Handler &h, const int version) { h & static_cast(*this) & static_cast(*this); - h & rewardType & rID & rVal & textOption & seerName; + h & rewardType & rID & rVal & textOption; } }; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 25e6c809f..af3bb98ac 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -966,12 +966,24 @@ CGHeroInstance *CGameState::getHero(int objid) return NULL; return static_cast(map->objects[objid]); } + +const CGHeroInstance * CGameState::getHero( int objid ) const +{ + return (const_cast(this))->getHero(objid); +} + CGTownInstance *CGameState::getTown(int objid) { if(objid<0 || objid>=map->objects.size()) return NULL; return static_cast(map->objects[objid]); } + +const CGTownInstance * CGameState::getTown( int objid ) const +{ + return (const_cast(this))->getTown(objid); +} + std::pair CGameState::pickObject (CGObjectInstance *obj) { switch(obj->ID) @@ -1162,6 +1174,7 @@ void CGameState::randomizeObject(CGObjectInstance *cur) if(cur->ID==TOWNI_TYPE) //town - set def { CGTownInstance *t = dynamic_cast(cur); + t->town = &VLC->townh->towns[t->subID]; if(t->hasCapitol()) t->defInfo = capitols[t->subID]; else if(t->hasFort()) @@ -1937,6 +1950,11 @@ PlayerState * CGameState::getPlayer( ui8 color ) } } +const PlayerState * CGameState::getPlayer( ui8 color ) const +{ + return (const_cast(this))->getPlayer(color); +} + bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret) { if(!map->isInTheMap(src) || !map->isInTheMap(dest)) //check input @@ -2916,6 +2934,191 @@ bool CGameState::battleCanShoot(int ID, int dest) return false; } +int CGameState::victoryCheck( ui8 player ) const +{ + const PlayerState *p = getPlayer(player); + if(map->victoryCondition.condition == winStandard || map->victoryCondition.allowNormalVictory) + if(player == checkForStandardWin()) + return -1; + + if(p->human || map->victoryCondition.appliesToAI) + { + switch(map->victoryCondition.condition) + { + case artifact: + //check if any hero has winning artifact + for(size_t i = 0; i < p->heroes.size(); i++) + if(p->heroes[i]->hasArt(map->victoryCondition.ID)) + return 1; + + break; + + case gatherTroop: + { + //check if in players armies there is enough creatures + int total = 0; //creature counter + for(size_t i = 0; i < map->objects.size(); i++) + { + const CArmedInstance *ai = NULL; + if(map->objects[i] + && map->objects[i]->tempOwner //object controlled by player + && (ai = dynamic_cast(map->objects[i]))) //contains army + { + for(TSlots::const_iterator i=ai->army.slots.begin(); i!=ai->army.slots.end(); ++i) //iterate through army + if(i->second.first == map->victoryCondition.ID) //it's searched creature + total += i->second.second; + } + } + + if(total >= map->victoryCondition.count) + return 1; + } + break; + + case gatherResource: + if(p->resources[map->victoryCondition.ID] >= map->victoryCondition.count) + return 1; + + break; + + case buildCity: + for(size_t i = 0; i < map->towns.size(); i++) + if(map->towns[i]->pos == map->victoryCondition.pos + && map->towns[i]->tempOwner == player + && map->towns[i]->hallLevel() >= map->victoryCondition.ID) + return 1; + break; + + case buildGrail: + for(size_t i = 0; i < map->towns.size(); i++) + if(map->towns[i]->pos == map->victoryCondition.pos + && map->towns[i]->tempOwner == player + && vstd::contains(map->towns[i]->builtBuildings, 26)) + return 1; + break; + + case beatHero: + if(map->victoryCondition.obj->tempOwner >= PLAYER_LIMIT) //target hero not present on map + return 1; + break; + case captureCity: + { + if(map->victoryCondition.obj->tempOwner == player) + return 1; + } + break; + case beatMonster: + if(!map->objects[map->victoryCondition.obj->id]) //target monster not present on map + return 1; + break; + case takeDwellings: + for(size_t i = 0; i < map->objects.size(); i++) + { + if(map->objects[i] && map->objects[i]->tempOwner != player) //check not flagged objs + { + switch(map->objects[i]->ID) + { + case 17: case 18: case 19: case 20: //dwellings + case 216: case 217: case 218: + return 0; //found not flagged dwelling - player not won + } + } + } + return 1; + break; + case takeMines: + for(size_t i = 0; i < map->objects.size(); i++) + { + if(map->objects[i] && map->objects[i]->tempOwner != player) //check not flagged objs + { + switch(map->objects[i]->ID) + { + case 53: case 220: + return 0; //found not flagged mine - player not won + } + } + } + return 1; + break; + case transportItem: + //TODO + break; + } + } + + return 0; +} + +ui8 CGameState::checkForStandardWin() const +{ + //std victory condition is: + //all enemies lost + ui8 supposedWinner = 255, winnerTeam = 255; + for(std::map::const_iterator i = players.begin(); i != players.end(); i++) + { + if(i->second.status == PlayerState::INGAME) + { + if(supposedWinner == 255) + { + //first player remaining ingame - candidate for victory + supposedWinner = i->second.color; + winnerTeam = map->players[supposedWinner].team; + } + else if(winnerTeam != map->players[i->second.color].team) + { + //current candidate has enemy remaining in game -> no vicotry + return 255; + } + } + } + + return supposedWinner; +} + +bool CGameState::checkForStandardLoss( ui8 player ) const +{ + //std loss condition is: player lost all towns and heroes + const PlayerState &p = *getPlayer(player); + return p.heroes.size() || p.towns.size(); +} + +int CGameState::lossCheck( ui8 player ) const +{ + const PlayerState *p = getPlayer(player); + if(map->lossCondition.typeOfLossCon == lossStandard) + if(checkForStandardLoss(player)) + return -1; + + if(p->human) //special loss condition applies only to human player + { + switch(map->lossCondition.typeOfLossCon) + { + case lossCastle: + { + const CGTownInstance *t = dynamic_cast(map->lossCondition.obj); + assert(t); + if(t->tempOwner != player) + return 1; + } + break; + case lossHero: + { + const CGHeroInstance *h = dynamic_cast(map->lossCondition.obj); + assert(h); + if(h->tempOwner != player) + return 1; + } + break; + case timeExpires: + if(map->lossCondition.timeLimit < day) + return 1; + break; + } + } + + return false; +} + const CStack * BattleInfo::getNextStack() const { std::vector hlp; @@ -3205,7 +3408,7 @@ CMP_stack::CMP_stack( int Phase /*= 1*/, int Turn ) } PlayerState::PlayerState() - : color(-1), currentSelection(0xffffffff) + : color(-1), currentSelection(0xffffffff), status(INGAME), daysWithoutCastle(0) { } diff --git a/lib/CGameState.h b/lib/CGameState.h index c2fb0c24d..602a20fe2 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -60,6 +60,7 @@ namespace boost struct DLL_EXPORT PlayerState { public: + enum EStatus {INGAME, LOSER, WINNER}; ui8 color, serial; ui8 human; //true if human controlled player, false for AI ui32 currentSelection; //id of hero/town, 0xffffffff if none @@ -70,42 +71,46 @@ public: std::vector availableHeroes; //heroes available in taverns std::vector dwellings; //used for town growth + ui8 status; //0 - in game, 1 - loser, 2 - winner <- uses EStatus enum + ui8 daysWithoutCastle; + PlayerState(); template void serialize(Handler &h, const int version) { - h & color & serial & human & currentSelection & fogOfWarMap & resources; + h & color & serial & human & currentSelection & fogOfWarMap & resources & status; + h & heroes & towns & availableHeroes & dwellings & status & daysWithoutCastle; - ui32 size; - if(h.saving) //write subids of available heroes - { - size = availableHeroes.size(); - h & size; - for(size_t i=0; i < size; i++) - { - if(availableHeroes[i]) - { - h & availableHeroes[i]->subID; - } - else - { - ui32 none = 0xffffffff; - h & none; - } - } - } - else - { - ui32 hid; - h & size; - for(size_t i=0; i < size; i++) - { - //fill availableHeroes with dummy hero instances, holding subids - h & hid; - availableHeroes.push_back(new CGHeroInstance); - availableHeroes[availableHeroes.size()-1]->subID = hid; - } - } +// ui32 size; +// if(h.saving) //write subids of available heroes +// { +// size = availableHeroes.size(); +// h & size; +// for(size_t i=0; i < size; i++) +// { +// if(availableHeroes[i]) +// { +// h & availableHeroes[i]->subID; +// } +// else +// { +// ui32 none = 0xffffffff; +// h & none; +// } +// } +// } +// else +// { +// ui32 hid; +// h & size; +// for(size_t i=0; i < size; i++) +// { +// //fill availableHeroes with dummy hero instances, holding subids +// h & hid; +// availableHeroes.push_back(new CGHeroInstance); +// availableHeroes[availableHeroes.size()-1]->subID = hid; +// } +// } } }; @@ -334,7 +339,7 @@ public: BattleInfo *curB; //current battle ui32 day; //total number of days in game Mapa * map; - std::map players; //ID <-> player state + std::map players; //ID <-> player state std::map villages, forts, capitols; //def-info for town graphics std::vector resVals; //default values of resources in gold @@ -354,6 +359,7 @@ public: boost::shared_mutex *mx; PlayerState *getPlayer(ui8 color); + const PlayerState *getPlayer(ui8 color) const; void init(StartInfo * si, Mapa * map, int Seed); void loadTownDInfos(); void randomizeObject(CGObjectInstance *cur); @@ -362,6 +368,8 @@ public: void apply(CPack *pack); CGHeroInstance *getHero(int objid); CGTownInstance *getTown(int objid); + const CGHeroInstance *getHero(int objid) const; + const CGTownInstance *getTown(int objid) const; bool battleMoveCreatureStack(int ID, int dest); bool battleAttackCreatureStack(int ID, int dest); bool battleShootCreatureStack(int ID, int dest); @@ -379,6 +387,10 @@ public: bool checkForVisitableDir(const int3 & src, const TerrainTile *pom, const int3 & dst) const; //check if src tile is visitable from dst tile bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret); //calculates path between src and dest; returns pointer to newly allocated CPath or NULL if path does not exists void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or NULL if path does not exists + int victoryCheck(ui8 player) const; //checks if given player is winner; -1 if std victory, 1 if special victory, 0 else + int lossCheck(ui8 player) const; //checks if given player is loser; -1 if std loss, 1 if special, 0 else + ui8 checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 if no winner + bool checkForStandardLoss(ui8 player) const; //checks if given player lost the game bool isVisible(int3 pos, int player); bool isVisible(const CGObjectInstance *obj, int player); @@ -395,31 +407,31 @@ public: { loadTownDInfos(); - //recreating towns/heroes vectors in players entries - for(int i=0; itowns.size(); i++) - if(map->towns[i]->tempOwner < PLAYER_LIMIT) - getPlayer(map->towns[i]->tempOwner)->towns.push_back(map->towns[i]); - for(int i=0; iheroes.size(); i++) - if(map->heroes[i]->tempOwner < PLAYER_LIMIT) - getPlayer(map->heroes[i]->tempOwner)->heroes.push_back(map->heroes[i]); - //recreating available heroes - for(std::map::iterator i=players.begin(); i!=players.end(); i++) - { - for(size_t j=0; j < i->second.availableHeroes.size(); j++) - { - ui32 hlp = i->second.availableHeroes[j]->subID; - delete i->second.availableHeroes[j]; - if(hlp != 0xffffffff) - { - assert(vstd::contains(hpool.heroesPool, hlp)); - i->second.availableHeroes[j] = hpool.heroesPool[hlp]; - } - else - { - i->second.availableHeroes[j] = NULL; - } - } - } +// //recreating towns/heroes vectors in players entries +// for(int i=0; itowns.size(); i++) +// if(map->towns[i]->tempOwner < PLAYER_LIMIT) +// getPlayer(map->towns[i]->tempOwner)->towns.push_back(map->towns[i]); +// for(int i=0; iheroes.size(); i++) +// if(map->heroes[i]->tempOwner < PLAYER_LIMIT) +// getPlayer(map->heroes[i]->tempOwner)->heroes.push_back(map->heroes[i]); +// //recreating available heroes +// for(std::map::iterator i=players.begin(); i!=players.end(); i++) +// { +// for(size_t j=0; j < i->second.availableHeroes.size(); j++) +// { +// ui32 hlp = i->second.availableHeroes[j]->subID; +// delete i->second.availableHeroes[j]; +// if(hlp != 0xffffffff) +// { +// assert(vstd::contains(hpool.heroesPool, hlp)); +// i->second.availableHeroes[j] = hpool.heroesPool[hlp]; +// } +// else +// { +// i->second.availableHeroes[j] = NULL; +// } +// } +// } } } diff --git a/lib/Connection.cpp b/lib/Connection.cpp index ae19a5823..73beb0aeb 100644 --- a/lib/Connection.cpp +++ b/lib/Connection.cpp @@ -147,22 +147,40 @@ CConnection::CConnection(boost::asio::basic_socket_acceptor @@ -205,6 +223,11 @@ void CConnection::setGS( CGameState *state ) gs = state; } +bool CConnection::isOpen() const +{ + return socket && connected; +} + CSaveFile::CSaveFile( const std::string &fname ) :sfile(new std::ofstream(fname.c_str(),std::ios::binary)) { diff --git a/lib/Connection.h b/lib/Connection.h index bc11b3060..6196738d5 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -11,16 +11,17 @@ #include #include #include -#include #include +#include +#include +#include #include #include #include #include -#include -const ui32 version = 714; +const ui32 version = 716; class CConnection; class CGObjectInstance; class CGameState; @@ -542,11 +543,11 @@ public: template void loadSerializable(T &data) { - ////that const cast would be evil because it allows to implicitly overwrite const objects when deserializing - //typedef typename boost::remove_const::type nonConstT; - //nonConstT &hlp = const_cast(data); - //hlp.serialize(*this,myVersion); - data.serialize(*this,myVersion); + ////that const cast is evil because it allows to implicitly overwrite const objects when deserializing + typedef typename boost::remove_const::type nonConstT; + nonConstT &hlp = const_cast(data); + hlp.serialize(*this,myVersion); + //data.serialize(*this,myVersion); } template void loadArray(T &data) @@ -587,7 +588,7 @@ public: This()->loadPointerHlp(tid, data); if(smartPointerSerialization && i == loadedPointers.end()) - loadedPointers[pid] = data; //add loaded pointer to our lookup map + loadedPointers[pid] = (void*)data; //add loaded pointer to our lookup map; cast is to avoid errors with const T* pt } //that part of ptr deserialization was extracted to allow customization of its behavior in derived classes @@ -597,7 +598,8 @@ public: if(!tid) { typedef typename boost::remove_pointer::type npT; - data = new npT; + typedef typename boost::remove_const::type ncpT; + data = new ncpT; *this >> *data; } else @@ -723,8 +725,8 @@ public: std::string Name); //use immediately after accepting connection into socket int write(const void * data, unsigned size); int read(void * data, unsigned size); - int readLine(void * data, unsigned maxSize); void close(); + bool isOpen() const; template CConnection &operator&(const T&); ~CConnection(void); diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 03c878058..10ce6b4f5 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -388,6 +388,26 @@ struct ChangeObjPos : public CPackForClient //116 } }; +struct PlayerEndsGame : public CPackForClient //117 +{ + PlayerEndsGame() + { + type = 117; + } + + void applyCl(CClient *cl); + DLL_EXPORT void applyGs(CGameState *gs); + + ui8 player; + ui8 victory; + + template void serialize(Handler &h, const int version) + { + h & player & victory; + } +}; + + struct RemoveObject : public CPackForClient //500 { RemoveObject(){type = 500;}; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 9bf7a14a7..3658b554c 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -219,6 +219,12 @@ DLL_EXPORT void ChangeObjPos::applyGs( CGameState *gs ) gs->map->addBlockVisTiles(obj); } +DLL_EXPORT void PlayerEndsGame::applyGs( CGameState *gs ) +{ + PlayerState *p = gs->getPlayer(player); + p->status = victory ? 2 : 1; +} + DLL_EXPORT void RemoveObject::applyGs( CGameState *gs ) { CGObjectInstance *obj = gs->map->objects[id]; @@ -230,6 +236,8 @@ DLL_EXPORT void RemoveObject::applyGs( CGameState *gs ) int player = h->tempOwner; nitr = std::find(gs->getPlayer(player)->heroes.begin(), gs->getPlayer(player)->heroes.end(), h); gs->getPlayer(player)->heroes.erase(nitr); + h->tempOwner = 255; //no one owns beaten hero + if(h->visitedTown) { if(h->inTownGarrison) diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index a5b3e8945..fe3c62650 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -79,6 +79,7 @@ void registerTypes2(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/map.cpp b/lib/map.cpp index ca2b7d9b5..5deb498ac 100644 --- a/lib/map.cpp +++ b/lib/map.cpp @@ -8,6 +8,7 @@ #include #include "../hch/CLodHandler.h" #include +#include /* * map.cpp, part of VCMI engine @@ -353,6 +354,7 @@ void CMapHeader::loadPlayerInfo( int &pom, const unsigned char * bufor, int &i ) void CMapHeader::loadViCLossConditions( const unsigned char * bufor, int &i) { + victoryCondition.obj = NULL; victoryCondition.condition = (EvictoryConditions)bufor[i++]; if (victoryCondition.condition != winStandard) //specific victory conditions { @@ -367,8 +369,8 @@ void CMapHeader::loadViCLossConditions( const unsigned char * bufor, int &i) } case gatherTroop: { - int temp1 = bufor[i+2]; - int temp2 = bufor[i+3]; +// int temp1 = bufor[i+2]; +// int temp2 = bufor[i+3]; victoryCondition.ID = bufor[i+2]; victoryCondition.count = readNormalNr(bufor, i+(version==RoE ? 3 : 4)); nr=(version==RoE ? 5 : 6); @@ -438,17 +440,11 @@ void CMapHeader::loadViCLossConditions( const unsigned char * bufor, int &i) switch (lossCondition.typeOfLossCon) //read loss conditions { case lossCastle: - { - lossCondition.castlePos.x=bufor[i++]; - lossCondition.castlePos.y=bufor[i++]; - lossCondition.castlePos.z=bufor[i++]; - break; - } case lossHero: { - lossCondition.heroPos.x=bufor[i++]; - lossCondition.heroPos.y=bufor[i++]; - lossCondition.heroPos.z=bufor[i++]; + lossCondition.pos.x=bufor[i++]; + lossCondition.pos.y=bufor[i++]; + lossCondition.pos.z=bufor[i++]; break; } case timeExpires: @@ -500,6 +496,9 @@ void Mapa::initFromBytes(const unsigned char * bufor) addBlockVisTiles(objects[f]); } tlog0<<"\tCalculating blocked/visitable tiles: "< &objs = getTile(victoryCondition.pos).visitableObjects; + assert(objs.size()); + victoryCondition.obj = objs.front(); + } + + if(isInTheMap(lossCondition.pos)) + { + const std::vector &objs = getTile(lossCondition.pos).visitableObjects; + assert(objs.size()); + lossCondition.obj = objs.front(); + } +} + void CMapInfo::countPlayers() { playerAmnt = humenPlayers = 0; @@ -2143,3 +2160,17 @@ void CMapInfo::init(const std::string &fname, const unsigned char *map ) initFromMemory(map, i); countPlayers(); } + +LossCondition::LossCondition() +{ + obj = NULL; + timeLimit = -1; + pos = int3(-1,-1,-1); +} + +CVictoryCondition::CVictoryCondition() +{ + pos = int3(-1,-1,-1); + obj = NULL; + ID = allowNormalVictory = appliesToAI = count = 0; +} \ No newline at end of file diff --git a/lib/map.h b/lib/map.h index 2578e251c..6ae74a471 100644 --- a/lib/map.h +++ b/lib/map.h @@ -137,14 +137,19 @@ struct DLL_EXPORT PlayerInfo struct DLL_EXPORT LossCondition { ElossCon typeOfLossCon; - int3 castlePos; - int3 heroPos; - int timeLimit; // in days + + int3 pos; + + si32 timeLimit; // in days; -1 if not used + const CGObjectInstance *obj; //set during map parsing: hero/town (depending on typeOfLossCon); NULL if not used + template void serialize(Handler &h, const int version) { - h & typeOfLossCon & castlePos & heroPos & timeLimit; + h & typeOfLossCon & pos & timeLimit & obj; } + + LossCondition(); }; struct DLL_EXPORT CVictoryCondition { @@ -155,10 +160,13 @@ struct DLL_EXPORT CVictoryCondition ui32 ID; //artifact ID (0); monster ID (1); resource ID (2); needed fort level in upgraded town (3); artifact ID (8) ui32 count; //needed count for creatures (1) / resource (2); upgraded town hall level (3); + const CGObjectInstance *obj; //object of specific monster / city / hero instance (NULL if not used); set during map parsing + + CVictoryCondition(); template void serialize(Handler &h, const int version) { - h & condition & allowNormalVictory & appliesToAI & pos & ID & count; + h & condition & allowNormalVictory & appliesToAI & pos & ID & count & obj; } }; @@ -329,6 +337,7 @@ struct DLL_EXPORT Mapa : public CMapHeader void loadTown( CGObjectInstance * &nobj, const unsigned char * bufor, int &i, int subid); int loadSeerHut( const unsigned char * bufor, int i, CGObjectInstance *& nobj); + void checkForObjectives(); void addBlockVisTiles(CGObjectInstance * obj); void removeBlockVisTiles(CGObjectInstance * obj, bool total=false); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b61bef738..8b718b18d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -491,6 +491,10 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer sg.garrs[bEndArmy2->id] = takeCasualties(bEndArmy2->tempOwner, bEndArmy2->army, gs->curB); sendAndApply(&sg); + ui8 sides[2]; + sides[0] = gs->curB->side1; + sides[1] = gs->curB->side2; + //end battle, remove all info, free memory giveExp(*battleResult.data); sendAndApply(battleResult.data); @@ -524,14 +528,17 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer // Necromancy if applicable. const CGHeroInstance *winnerHero = battleResult.data->winner != 0 ? hero2 : hero1; - if (winnerHero) { + if (winnerHero) + { std::pair raisedStack = winnerHero->calculateNecromancy(*battleResult.data); // Give raised units to winner and show dialog, if any were raised. - if (raisedStack.first != -1) { + if (raisedStack.first != -1) + { int slot = winnerHero->army.getSlotFor(raisedStack.first); - if (slot != -1) { + if (slot != -1) + { SetGarrisons sg; sg.garrs[winnerHero->id] = winnerHero->army; @@ -545,6 +552,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer } } + winLoseHandle(1< players, CConnection &c) CPack *pack = NULL; try { - while(!end2) + while(1)//server should never shut connection first //was: while(!end2) { { boost::unique_lock lock(*c.rmx); @@ -640,11 +648,12 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) } catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection { + assert(!c.connected); //make sure that connection has been marked as broken tlog1 << e.what() << std::endl; end2 = true; } HANDLE_EXCEPTION(end2 = true); -handleConEnd: + tlog1 << "Ended handling connection\n"; } @@ -901,6 +910,8 @@ void CGameHandler::newTurn() for(size_t i = 0; imap->objects.size(); i++) if(gs->map->objects[i]) gs->map->objects[i]->newTurn(); + + winLoseHandle(0xff); } void CGameHandler::run(bool resume) { @@ -948,7 +959,13 @@ void CGameHandler::run(bool resume) for(; i != gs->players.end(); i++) { - if((i->second.towns.size()==0 && i->second.heroes.size()==0) || i->second.color<0 || i->first>=PLAYER_LIMIT ) continue; //players has not towns/castle - loser + if(i->second.towns.size()==0 && i->second.heroes.size()==0 + || i->second.color<0 + || i->first>=PLAYER_LIMIT + || i->second.status) + { + continue; + } states.setFlag(i->first,&PlayerStatus::makingTurn,true); gs->currentPlayer = i->first; { @@ -968,6 +985,9 @@ void CGameHandler::run(bool resume) } } } + + while(conns.size() && (*conns.begin())->isOpen()) + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); //give time client to close socket } namespace CGH @@ -1385,6 +1405,8 @@ bool CGameHandler::removeObject( int objid ) RemoveObject ro; ro.id = objid; sendAndApply(&ro); + + winLoseHandle(255); //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function) return true; } @@ -1556,6 +1578,8 @@ void CGameHandler::setOwner(int objid, ui8 owner) { SetObjectProperty sop(objid,1,owner); sendAndApply(&sop); + + winLoseHandle(1<getPlayer(i)) + { + checkLossVictory(i); + } + } +} + +void CGameHandler::checkLossVictory( ui8 player ) +{ + int loss = gs->lossCheck(player); + int vic = gs->victoryCheck(player); + + if(!loss && !vic) + return; + + InfoWindow iw; + getLossVicMessage(player, vic ? vic < 0 : loss < 0, vic, iw); + sendAndApply(&iw); + + PlayerEndsGame peg; + peg.player = player; + peg.victory = vic; + sendAndApply(&peg); + + if(vic) + end2 = true; +} + +void CGameHandler::getLossVicMessage( ui8 player, bool standard, bool victory, InfoWindow &out ) const +{ + const PlayerState *p = gs->getPlayer(player); + if(!p->human) + return; //AI doesn't need text info of loss + + out.player = player; + + if(victory) + { + if(!standard) //not std loss + { + switch(gs->map->victoryCondition.condition) + { + case artifact: + out.text.addTxt(MetaString::GENERAL_TXT, 280); //Congratulations! You have found the %s, and can claim victory! + out.text.addReplacement(MetaString::ART_NAMES,gs->map->victoryCondition.obj->subID); //artifact name + break; + case gatherTroop: + out.text.addTxt(MetaString::GENERAL_TXT, 276); //Congratulations! You have over %d %s in your armies. Your enemies have no choice but to bow down before your power! + out.text.addReplacement(gs->map->victoryCondition.count); + out.text.addReplacement(MetaString::CRE_PL_NAMES, gs->map->victoryCondition.ID); + break; + case gatherResource: + out.text.addTxt(MetaString::GENERAL_TXT, 278); //Congratulations! You have collected over %d %s in your treasury. Victory is yours! + out.text.addReplacement(gs->map->victoryCondition.count); + out.text.addReplacement(MetaString::RES_NAMES, gs->map->victoryCondition.ID); + break; + case buildCity: + out.text.addTxt(MetaString::GENERAL_TXT, 282); //Congratulations! You have successfully upgraded your town, and can claim victory! + break; + case buildGrail: + out.text.addTxt(MetaString::GENERAL_TXT, 284); //Congratulations! You have constructed a permanent home for the Grail, and can claim victory! + break; + case beatHero: + { + out.text.addTxt(MetaString::GENERAL_TXT, 252); //Congratulations! You have completed your quest to defeat the enemy hero %s. Victory is yours! + const CGHeroInstance *h = dynamic_cast(gs->map->victoryCondition.obj); + assert(h); + out.text.addReplacement(h->name); + } + break; + case captureCity: + { + out.text.addTxt(MetaString::GENERAL_TXT, 249); //Congratulations! You captured %s, and are victorious! + const CGTownInstance *t = dynamic_cast(gs->map->victoryCondition.obj); + assert(t); + out.text.addReplacement(t->name); + } + break; + case beatMonster: + out.text.addTxt(MetaString::GENERAL_TXT, 286); //Congratulations! You have completed your quest to kill the fearsome beast, and can claim victory! + break; + case takeDwellings: + out.text.addTxt(MetaString::GENERAL_TXT, 288); //Congratulations! Your flag flies on the dwelling of every creature. Victory is yours! + break; + case takeMines: + out.text.addTxt(MetaString::GENERAL_TXT, 290); //Congratulations! Your flag flies on every mine. Victory is yours! + break; + case transportItem: + out.text.addTxt(MetaString::GENERAL_TXT, 292); //Congratulations! You have reached your destination, precious cargo intact, and can claim victory! + break; + } + } + else + { + + } + } + else + { + if(!standard) //not std loss + { + switch(gs->map->lossCondition.typeOfLossCon) + { + case lossCastle: + { + out.text.addTxt(MetaString::GENERAL_TXT, 251); //The town of %s has fallen - all is lost! + const CGTownInstance *t = dynamic_cast(gs->map->lossCondition.obj); + assert(t); + out.text.addReplacement(t->name); + } + break; + case lossHero: + { + out.text.addTxt(MetaString::GENERAL_TXT, 253); //The hero, %s, has suffered defeat - your quest is over! + const CGHeroInstance *h = dynamic_cast(gs->map->lossCondition.obj); + assert(h); + out.text.addReplacement(h->name); + } + break; + case timeExpires: + out.text.addTxt(MetaString::GENERAL_TXT, 254); //Alas, time has run out on your quest. All is lost. + break; + } + } + else //lost all towns and heroes + { + out.text.addReplacement(MetaString::GENERAL_TXT, 660); //All your forces have been defeated, and you are banished from this land! + } + } +} \ No newline at end of file diff --git a/server/CGameHandler.h b/server/CGameHandler.h index d2d974fe1..d1009b458 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -85,6 +85,10 @@ public: void giveSpells(const CGTownInstance *t, const CGHeroInstance *h); int moveStack(int stack, int dest); //returned value - travelled distance void startBattle(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero + void checkLossVictory(ui8 player); + void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all + void getLossVicMessage(ui8 player, bool standard, bool victory, InfoWindow &out) const; + ////used only in endBattle - don't touch elsewhere boost::function * battleEndCallback; const CArmedInstance * bEndArmy1, * bEndArmy2; @@ -95,7 +99,6 @@ public: void checkForBattleEnd( std::vector &stacks ); void setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet &army1, const CCreatureSet &army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town); - CGameHandler(void); ~CGameHandler(void);