1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

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.
This commit is contained in:
Michał W. Urbańczyk 2010-01-29 20:52:45 +00:00
parent 8447e58c39
commit 5279e2e9fc
23 changed files with 643 additions and 105 deletions

View File

@ -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<CSaveFile> &h, const int version){}; //saving
virtual void serialize(CISer<CLoadFile> &h, const int version){}; //loading

View File

@ -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;

View File

@ -746,7 +746,7 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
pom.push_back(std::pair<std::string,CFunctionList<void()> >("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<soundBase::soundID>(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;

View File

@ -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<CSaveFile> &h, const int version); //saving
void serialize(CISer<CLoadFile> &h, const int version); //loading

View File

@ -14,6 +14,7 @@
#include "../hch/CLodHandler.h"
#include "../hch/CTownHandler.h"
#include "../hch/CHeroHandler.h"
#include "../hch/CObjectHandler.h"
#include <cmath>
#include "Graphics.h"
//#include <boost/thread.hpp>

View File

@ -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()

View File

@ -628,6 +628,7 @@ void CGarrisonInt::deactivate()
CInfoWindow::CInfoWindow(std::string text, int player, int charperline, const std::vector<SComponent*> &comps, std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, bool delComps)
{
slider = NULL;
ID = -1;
this->delComps = delComps;
for(int i=0;i<Buttons.size();i++)

View File

@ -127,6 +127,12 @@ void ChangeObjPos::applyCl( CClient *cl )
CGI->mh->printObject(obj);
}
void PlayerEndsGame::applyCl( CClient *cl )
{
for(std::map<ui8, CGameInterface*>::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);

View File

@ -325,6 +325,10 @@
RelativePath=".\CHeroWindow.cpp"
>
</File>
<File
RelativePath=".\CKingdomInterface.cpp"
>
</File>
<File
RelativePath=".\Client.cpp"
>
@ -471,6 +475,10 @@
RelativePath=".\CHeroWindow.h"
>
</File>
<File
RelativePath=".\CKingdomInterface.h"
>
</File>
<File
RelativePath=".\Client.h"
>

View File

@ -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

View File

@ -1234,6 +1234,17 @@ void CGHeroInstance::recreateArtBonuses()
}
}
bool CGHeroInstance::hasArt( ui32 aid ) const
{
if(vstd::contains(artifacts, aid))
return true;
for(std::map<ui16,ui32>::const_iterator i = artifWorn.begin(); i != artifWorn.end(); i++)
if(i->second == aid)
return true;
return true;
}
void CGDwelling::initObj()
{
switch(ID)

View File

@ -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 <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CGObjectInstance&>(*this) & static_cast<CQuest&>(*this);
h & rewardType & rID & rVal & textOption & seerName;
h & rewardType & rID & rVal & textOption;
}
};

View File

@ -966,12 +966,24 @@ CGHeroInstance *CGameState::getHero(int objid)
return NULL;
return static_cast<CGHeroInstance *>(map->objects[objid]);
}
const CGHeroInstance * CGameState::getHero( int objid ) const
{
return (const_cast<CGameState *>(this))->getHero(objid);
}
CGTownInstance *CGameState::getTown(int objid)
{
if(objid<0 || objid>=map->objects.size())
return NULL;
return static_cast<CGTownInstance *>(map->objects[objid]);
}
const CGTownInstance * CGameState::getTown( int objid ) const
{
return (const_cast<CGameState *>(this))->getTown(objid);
}
std::pair<int,int> 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<CGTownInstance*>(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<CGameState *>(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<const CArmedInstance*>(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<ui8,PlayerState>::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<const CGTownInstance *>(map->lossCondition.obj);
assert(t);
if(t->tempOwner != player)
return 1;
}
break;
case lossHero:
{
const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(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<const CStack *> 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)
{
}

View File

@ -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<CGHeroInstance *> availableHeroes; //heroes available in taverns
std::vector<CGDwelling *> dwellings; //used for town growth
ui8 status; //0 - in game, 1 - loser, 2 - winner <- uses EStatus enum
ui8 daysWithoutCastle;
PlayerState();
template <typename Handler> 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<ui8,PlayerState> players; //ID <-> player state
std::map<ui8, PlayerState> players; //ID <-> player state
std::map<int, CGDefInfo*> villages, forts, capitols; //def-info for town graphics
std::vector<ui32> 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; i<map->towns.size(); i++)
if(map->towns[i]->tempOwner < PLAYER_LIMIT)
getPlayer(map->towns[i]->tempOwner)->towns.push_back(map->towns[i]);
for(int i=0; i<map->heroes.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<ui8,PlayerState>::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; i<map->towns.size(); i++)
// if(map->towns[i]->tempOwner < PLAYER_LIMIT)
// getPlayer(map->towns[i]->tempOwner)->towns.push_back(map->towns[i]);
// for(int i=0; i<map->heroes.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<ui8,PlayerState>::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;
// }
// }
// }
}
}

View File

@ -147,22 +147,40 @@ CConnection::CConnection(boost::asio::basic_socket_acceptor<boost::asio::ip::tcp
int CConnection::write(const void * data, unsigned size)
{
//LOG("Sending " << size << " byte(s) of data" <<std::endl);
int ret;
ret = asio::write(*socket,asio::const_buffers_1(asio::const_buffer(data,size)));
return ret;
try
{
int ret;
ret = asio::write(*socket,asio::const_buffers_1(asio::const_buffer(data,size)));
return ret;
}
catch(...)
{
//connection has been lost
connected = false;
throw;
}
}
int CConnection::read(void * data, unsigned size)
{
//LOG("Receiving " << size << " byte(s) of data" <<std::endl);
int ret = asio::read(*socket,asio::mutable_buffers_1(asio::mutable_buffer(data,size)));
return ret;
try
{
int ret = asio::read(*socket,asio::mutable_buffers_1(asio::mutable_buffer(data,size)));
return ret;
}
catch(...)
{
//connection has been lost
connected = false;
throw;
}
}
CConnection::~CConnection(void)
{
close();
delete io_service;
delete wmx;
delete rmx;
delete io_service;
delete wmx;
delete rmx;
}
template<class T>
@ -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))
{

View File

@ -11,16 +11,17 @@
#include <boost/type_traits/is_enum.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/is_array.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/type_traits/is_array.hpp>
const ui32 version = 714;
const ui32 version = 716;
class CConnection;
class CGObjectInstance;
class CGameState;
@ -542,11 +543,11 @@ public:
template <typename T>
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<T>::type nonConstT;
//nonConstT &hlp = const_cast<nonConstT&>(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<T>::type nonConstT;
nonConstT &hlp = const_cast<nonConstT&>(data);
hlp.serialize(*this,myVersion);
//data.serialize(*this,myVersion);
}
template <typename T>
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<T>::type npT;
data = new npT;
typedef typename boost::remove_const<npT>::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<class T>
CConnection &operator&(const T&);
~CConnection(void);

View File

@ -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 <typename Handler> void serialize(Handler &h, const int version)
{
h & player & victory;
}
};
struct RemoveObject : public CPackForClient //500
{
RemoveObject(){type = 500;};

View File

@ -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)

View File

@ -79,6 +79,7 @@ void registerTypes2(Serializer &s)
s.template registerType<SetAvailableHeroes>();
s.template registerType<GiveBonus>();
s.template registerType<ChangeObjPos>();
s.template registerType<PlayerEndsGame>();
s.template registerType<RemoveObject>();
s.template registerType<TryMoveHero>();
s.template registerType<SetGarrisons>();

View File

@ -8,6 +8,7 @@
#include <boost/crc.hpp>
#include "../hch/CLodHandler.h"
#include <boost/bind.hpp>
#include <assert.h>
/*
* 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: "<<th.getDif()<<std::endl;
checkForObjectives();
tlog0 << "\tMap initialization done!" << std::endl;
}
void Mapa::removeBlockVisTiles(CGObjectInstance * obj, bool total)
{
@ -1402,6 +1401,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
pos.x = bufor[i++];
pos.y = bufor[i++];
pos.z = bufor[i++];
int defnum = readNormalNr(bufor,i, 4); i+=4;
CGDefInfo * defInfo = defy[defnum];
@ -2109,6 +2109,23 @@ bool Mapa::isWaterTile(const int3 &pos) const
return isInTheMap(pos) && getTile(pos).tertype == TerrainTile::water;
}
void Mapa::checkForObjectives()
{
if(isInTheMap(victoryCondition.pos))
{
const std::vector <CGObjectInstance*> &objs = getTile(victoryCondition.pos).visitableObjects;
assert(objs.size());
victoryCondition.obj = objs.front();
}
if(isInTheMap(lossCondition.pos))
{
const std::vector <CGObjectInstance*> &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;
}

View File

@ -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 <typename Handler> 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 <typename Handler> 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);

View File

@ -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<ui32, si32> 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<<sides[0] | 1<<sides[1]); //handle victory/loss of engaged players
delete battleResult.data;
}
@ -602,7 +610,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
CPack *pack = NULL;
try
{
while(!end2)
while(1)//server should never shut connection first //was: while(!end2)
{
{
boost::unique_lock<boost::mutex> lock(*c.rmx);
@ -640,11 +648,12 @@ void CGameHandler::handleConnection(std::set<int> 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; i<gs->map->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<<owner);
}
void CGameHandler::setHoverName(int objid, MetaString* name)
{
@ -3433,3 +3457,138 @@ void CGameHandler::engageIntoBattle( ui8 player )
pb.reason = PlayerBlocked::UPCOMING_BATTLE;
sendAndApply(&pb);
}
void CGameHandler::winLoseHandle(ui8 players )
{
for(size_t i = 0; i < PLAYER_LIMIT; i++)
{
if(players & 1<<i && gs->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<const CGHeroInstance*>(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<const CGTownInstance*>(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<const CGTownInstance*>(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<const CGHeroInstance*>(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!
}
}
}

View File

@ -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<void(BattleResult*)> 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<void(BattleResult*)> * battleEndCallback;
const CArmedInstance * bEndArmy1, * bEndArmy2;
@ -95,7 +99,6 @@ public:
void checkForBattleEnd( std::vector<CStack*> &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);