Pārlūkot izejas kodu

* further fight with hanged scroll tab
* fixed r-click on FoW crash
* fixed crash on opening tavern window after load
* fixed recruiting new heroes after load
* fixed crash on loading map with spell scrolls
* fixed more crashes
* displpaying difficulty level of saved game
* added support for Library of Enlightenment
* added notification when saving is done

Michał W. Urbańczyk 16 gadi atpakaļ
vecāks
revīzija
3d29c31deb

+ 4 - 0
AdventureMapButton.cpp

@@ -371,6 +371,10 @@ void CSlider::clickLeft (tribool down)
 		moveTo(rw*amount);
 		return;
 	}
+	else
+	{
+		moving = false;
+	}
 	if(moving)
 	{
 		MotionInterested::deactivate();

+ 10 - 0
CAdvmapInterface.cpp

@@ -454,6 +454,11 @@ void CTerrainRect::clickRight(tribool down)
 	{
 	case 34:
 		{
+			if(!vstd::contains(graphics->heroWins,obj->subID))
+			{
+				tlog3 << "Warning - no infowin for hero " << obj->subID << std::endl;
+				break;
+			}
 			CInfoPopup * ip = new CInfoPopup(graphics->heroWins[obj->subID],
 				LOCPLINT->current->motion.x-graphics->heroWins[obj->subID]->w,
 				LOCPLINT->current->motion.y-graphics->heroWins[obj->subID]->h,false
@@ -463,6 +468,11 @@ void CTerrainRect::clickRight(tribool down)
 		}
 	case 98:
 		{
+			if(!vstd::contains(graphics->townWins,obj->subID))
+			{
+				tlog3 << "Warning - no infowin for town " << obj->subID << std::endl;
+				break;
+			}
 			CInfoPopup * ip = new CInfoPopup(graphics->townWins[obj->id],
 				LOCPLINT->current->motion.x-graphics->townWins[obj->id]->w,
 				LOCPLINT->current->motion.y-graphics->townWins[obj->id]->h,false

+ 5 - 5
CCallback.cpp

@@ -574,9 +574,9 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, int aid)
 std::vector < const CGObjectInstance * > CCallback::getBlockingObjs( int3 pos ) const
 {
 	std::vector<const CGObjectInstance *> ret;
-	if(!gs->map->isInTheMap(pos))
-		return ret;
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	if(!gs->map->isInTheMap(pos) || !isVisible(pos))
+		return ret;
 	BOOST_FOREACH(const CGObjectInstance * obj, gs->map->terrain[pos.x][pos.y][pos.z].blockingObjects)
 		ret.push_back(obj);
 	return ret;
@@ -585,9 +585,9 @@ std::vector < const CGObjectInstance * > CCallback::getBlockingObjs( int3 pos )
 std::vector < const CGObjectInstance * > CCallback::getVisitableObjs( int3 pos ) const
 {
 	std::vector<const CGObjectInstance *> ret;
-	if(!gs->map->isInTheMap(pos))
-		return ret;
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	if(!gs->map->isInTheMap(pos) || !isVisible(pos))
+		return ret;
 	BOOST_FOREACH(const CGObjectInstance * obj, gs->map->terrain[pos.x][pos.y][pos.z].visitableObjects)
 		ret.push_back(obj);
 	return ret;
@@ -614,7 +614,7 @@ void CCallback::getMarketOffer( int t1, int t2, int &give, int &rec, int mode/*=
 
 std::vector < const CGObjectInstance * > CCallback::getFlaggableObjects(int3 pos) const
 {
-	if(!isVisible(pos, LOCPLINT->playerID))
+	if(!isVisible(pos))
 		return std::vector < const CGObjectInstance * >();
 
 	std::vector < const CGObjectInstance * > ret;

+ 2 - 0
CGameState.cpp

@@ -721,6 +721,8 @@ void CGameState::applyNL(IPack * pack)
 			}
 			else
 				map->objects[h->id] = h;
+
+			h->initHeroDefInfo();
 			map->heroes.push_back(h);
 			players[h->tempOwner].heroes.push_back(h);
 			map->addBlockVisTiles(h);

+ 31 - 0
CGameState.h

@@ -60,6 +60,27 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & color & serial & currentSelection & fogOfWarMap & resources;
+
+		ui32 size;
+		if(h.saving) //write subids of available heroes
+		{
+			size = availableHeroes.size();
+			h & size;
+			for(size_t i=0; i < size; i++)
+				h & availableHeroes[i]->subID;
+		}
+		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;
+			}
+		}
 	}
 };
 
@@ -225,6 +246,16 @@ public:
 			for(int i=0; i<map->heroes.size(); i++)
 				if(map->heroes[i]->tempOwner < PLAYER_LIMIT)
 					players[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];
+					i->second.availableHeroes[j] = hpool.heroesPool[hlp];
+				}
+			}
 		}
 	}
 

+ 31 - 6
CPreGame.cpp

@@ -1074,12 +1074,18 @@ void MapSel::processMaps(std::vector<std::string> &pliczkiTemp, int &index)
 void MapSel::processGames( std::vector<std::string> &pliczkiTemp, int &index )
 {
 	ourGames.resize(pliczkiTemp.size());
+	ui32 hlp;
 
 	for(int i=0; i<pliczkiTemp.size(); i++)
 	{
 		CLoadFile lf(pliczkiTemp[i]);
 		ui8 sign[8]; 
-		lf >> sign >> static_cast<CMapHeader&>(ourGames[i]);
+		lf >> sign >> hlp >> static_cast<CMapHeader&>(ourGames[i]) >> ourGames[i].seldiff;
+		if(hlp != version)
+		{
+			tlog3 << "\t" << ourGames[i].filename << " seems to be too " << ((hlp>version) ? "new" : "old") << " and will be ommited.\n";
+			continue;
+		}
 		ourGames[i].filename = pliczkiTemp[i];
 		ourGames[i].countPlayers();
 	}
@@ -1308,6 +1314,8 @@ MapSel::MapSel():selected(0),sizeFilter(0)
 }
 void MapSel::printSelectedInfo()
 {
+	CMapInfo &selMap = selectedMap();
+
 	SDL_BlitSurface(CPG->ourScenSel->scenInf,&genRect(399,337,17,23),screen,&genRect(399,337,413,29));
 	SDL_BlitSurface(CPG->ourScenSel->scenInf,&genRect(50,91,18,447),screen,&genRect(50,91,414,453));
 	if(CPG->fromnewgame)
@@ -1316,6 +1324,19 @@ void MapSel::printSelectedInfo()
 		SDL_BlitSurface(CPG->ourScenSel->bOptions.imgs->ourImages[0].bitmap,NULL,screen,&CPG->ourScenSel->bOptions.pos);
 		SDL_BlitSurface(CPG->ourScenSel->bRandom.imgs->ourImages[0].bitmap,NULL,screen,&CPG->ourScenSel->bRandom.pos);
 	}
+	else
+	{
+		CPG->ourScenSel->bEasy.state = 2 + (selMap.seldiff==0);
+		CPG->ourScenSel->bNormal.state = 2 + (selMap.seldiff==1);
+		CPG->ourScenSel->bHard.state = 2 + (selMap.seldiff==2);
+		CPG->ourScenSel->bExpert.state = 2 + (selMap.seldiff==3);
+		CPG->ourScenSel->bImpossible.state = 2 + (selMap.seldiff==4);
+		CPG->ourScenSel->bEasy.show();
+		CPG->ourScenSel->bNormal.show();
+		CPG->ourScenSel->bHard.show();
+		CPG->ourScenSel->bExpert.show();
+		CPG->ourScenSel->bImpossible.show();
+	}
 	//blit texts
 	CSDL_Ext::printAt(CGI->generaltexth->zelp[21].second,420,25,GEOR13);
 	CSDL_Ext::printAt(CGI->generaltexth->allTexts[496],420,135,GEOR13);
@@ -1324,8 +1345,6 @@ void MapSel::printSelectedInfo()
 	CSDL_Ext::printAt(CGI->generaltexth->allTexts[390],420,406,GEOR13,zwykly);
 	CSDL_Ext::printAt(CGI->generaltexth->allTexts[391],585,406,GEOR13,zwykly);
 
-	CMapInfo &selMap = selectedMap();
-
 	int temp = selMap.victoryCondition.condition+1;
 	if (temp>20) temp=0;
 	std::string sss = CGI->generaltexth->victoryConditions[temp];
@@ -2165,13 +2184,15 @@ StartInfo CPreGame::runLoop()
 					{
 						highlightButton(1,2);
 						current->highlighted=1;
-						(this->*(current->fNewGame))();
+						if(current->fNewGame)
+							(this->*(current->fNewGame))();
 					}
 					else if (isItIn(&current->lLoadGame,sEvent.motion.x,sEvent.motion.y))
 					{
 						highlightButton(2,2);
 						current->highlighted=2;
-						(this->*(current->fLoadGame))();
+						if(current->fLoadGame)
+							(this->*(current->fLoadGame))();
 					}
 					else if (isItIn(&current->lHighScores,sEvent.motion.x,sEvent.motion.y))
 					{
@@ -2187,7 +2208,8 @@ StartInfo CPreGame::runLoop()
 					{
 						highlightButton(5,2);
 						current->highlighted=5;
-						(this->*(current->fQuit))();
+						if(current->fQuit)
+							(this->*(current->fQuit))();
 					}
 				}
 				else if ((sEvent.type==SDL_MOUSEBUTTONDOWN) && (sEvent.button.button == SDL_BUTTON_RIGHT))
@@ -2376,16 +2398,19 @@ void CPreGame::initLoadMenu()
 	ourLoadMenu->lLoadGame.w=ourLoadMenu->loadGame->ourImages[0].bitmap->w;
 	ourLoadMenu->lLoadGame.x=568;
 	ourLoadMenu->lLoadGame.y=120;
+	ourLoadMenu->fLoadGame = NULL;
 	//campaign
 	ourLoadMenu->lHighScores.h=ourLoadMenu->highScores->ourImages[0].bitmap->h;
 	ourLoadMenu->lHighScores.w=ourLoadMenu->highScores->ourImages[0].bitmap->w;
 	ourLoadMenu->lHighScores.x=541;
 	ourLoadMenu->lHighScores.y=233;
+	ourLoadMenu->fHighScores = NULL;
 	//tutorial
 	ourLoadMenu->lCredits.h=ourLoadMenu->credits->ourImages[0].bitmap->h;
 	ourLoadMenu->lCredits.w=ourLoadMenu->credits->ourImages[0].bitmap->w;
 	ourLoadMenu->lCredits.x=545;
 	ourLoadMenu->lCredits.y=358;
+	ourLoadMenu->fCredits = NULL;
 	//back
 	ourLoadMenu->lQuit.h=ourLoadMenu->quit->ourImages[0].bitmap->h;
 	ourLoadMenu->lQuit.w=ourLoadMenu->quit->ourImages[0].bitmap->w;

+ 1 - 1
StartInfo.h

@@ -34,7 +34,7 @@ struct StartInfo
 		}
 	};
 	ui8 mode; //0 - new game; 1 - load game
-	si32 difficulty; //0=easy; 4=impossible
+	ui8 difficulty; //0=easy; 4=impossible
 	std::vector<PlayerSettings> playerInfos;
 	ui8 turnTime; //in minutes, 0=unlimited
 	std::string mapname;

+ 10 - 1
client/Client.cpp

@@ -31,6 +31,7 @@ namespace intpr = boost::interprocess;
 
 void CClient::init()
 {
+	IObjectInterface::cb = this;
 	serv = NULL;
 	gs = NULL;
 	cb = NULL;
@@ -58,6 +59,13 @@ void CClient::process(int what)
 	static BattleAction curbaction;
 	switch (what)
 	{
+	case 95: //system message
+		{
+			std::string m;
+			*serv >> m;
+			tlog4 << "System message from server: " << m << std::endl;
+			break;
+		}
 	case 100: //one of our interfaces has turn
 		{
 			ui8 player;
@@ -635,12 +643,13 @@ void CClient::load( const std::string & fname )
 	tlog0 <<"Restarting server: "<<tmh.getDif()<<std::endl;
 
 	{
+		ui32 ver;
 		char sig[8];
 		CMapHeader dum;
 		CGI->mh = new CMapHandler();
 
 		CLoadFile lf(fname + ".vlgm1");
-		lf >> sig >> dum;
+		lf >> sig >> ver >> dum >> *sig;
 		tlog0 <<"Reading save signature: "<<tmh.getDif()<<std::endl;
 			
 		lf >> *VLC;

+ 52 - 18
hch/CObjectHandler.cpp

@@ -475,25 +475,9 @@ void CGHeroInstance::initHero(int SUBID)
 
 void CGHeroInstance::initHero()
 {
-	if(!defInfo)
-	{
-		defInfo = new CGDefInfo();
-		defInfo->id = 34;
-		defInfo->subid = subID;
-		defInfo->printPriority = 0;
-		defInfo->visitDir = 0xff;
-	}
+	initHeroDefInfo();
 	if(!type)
 		type = VLC->heroh->heroes[subID];
-	for(int i=0;i<6;i++)
-	{
-		defInfo->blockMap[i]=255;
-		defInfo->visitMap[i]=0;
-	}
-	defInfo->handler=NULL;
-	defInfo->blockMap[5] = 253;
-	defInfo->visitMap[5] = 2;
-
 	artifWorn[16] = 3;
 	if(type->heroType % 2 == 1) //it's a magical hero
 	{
@@ -559,6 +543,25 @@ void CGHeroInstance::initHero()
 	boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name);
 }
 
+void CGHeroInstance::initHeroDefInfo()
+{
+	if(!defInfo)
+	{
+		defInfo = new CGDefInfo();
+		defInfo->id = 34;
+		defInfo->subid = subID;
+		defInfo->printPriority = 0;
+		defInfo->visitDir = 0xff;
+	}
+	for(int i=0;i<6;i++)
+	{
+		defInfo->blockMap[i]=255;
+		defInfo->visitMap[i]=0;
+	}
+	defInfo->handler=NULL;
+	defInfo->blockMap[5] = 253;
+	defInfo->visitMap[5] = 2;
+}
 CGHeroInstance::~CGHeroInstance()
 {
 }
@@ -597,6 +600,7 @@ void CGHeroInstance::initObj()
 {
 	blockVisit = true;
 }
+
 int CGTownInstance::getSightDistance() const //returns sight distance
 {
 	return 10;
@@ -743,7 +747,7 @@ void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
 	if(visitors.find(h->id)==visitors.end())
 	{
 		onNAHeroVisit(h->id, false);
-		if(ID != 102  &&  ID!=4) //not tree nor arena
+		if(ID != 102  &&  ID!=4  && ID!=41) //not tree nor arena nor library of enlightenment
 			cb->setObjProperty(id,4,h->id); //add to the visitors
 	}
 	else
@@ -804,6 +808,9 @@ void CGVisitableOPH::onNAHeroVisit(int heroID, bool alreadyVisited) const
 		ot = 146;
 		val = 1;
 		break;
+	case 41:
+		ot = 66;
+		break;
 	}
 	if (!alreadyVisited)
 	{
@@ -891,6 +898,30 @@ void CGVisitableOPH::onNAHeroVisit(int heroID, bool alreadyVisited) const
 				}
 				break;
 			}
+		case 41:
+			{
+				const CGHeroInstance *h = cb->getHero(heroID);
+				if(h->level  <  10 - 2*h->getSecSkillLevel(4)) //not enough level
+				{
+					InfoWindow iw;
+					iw.player = cb->getOwner(heroID);
+					iw.text << std::pair<ui8,ui32>(11,68);
+					cb->showInfoDialog(&iw);
+				}
+				else
+				{
+					cb->setObjProperty(this->id,4,heroID); //add to the visitors
+					cb->changePrimSkill(heroID,0,2);
+					cb->changePrimSkill(heroID,1,2);
+					cb->changePrimSkill(heroID,2,2);
+					cb->changePrimSkill(heroID,3,2);
+					InfoWindow iw;
+					iw.player = cb->getOwner(heroID);
+					iw.text << std::pair<ui8,ui32>(11,66);
+					cb->showInfoDialog(&iw);
+				}
+				break;
+			}
 		}
 	}
 	else
@@ -929,6 +960,8 @@ const std::string & CGVisitableOPH::getHoverText() const
 	case 102:
 		pom = 18;
 		break;
+	case 41:
+		break;
 	default:
 		throw std::string("Wrong CGVisitableOPH object ID!\n");
 	}
@@ -1276,6 +1309,7 @@ void CGArtifact::initObj()
 void CGArtifact::onHeroVisit( const CGHeroInstance * h ) const
 {
 	cb->giveHeroArtifact(subID,h->id,-2);
+	cb->removeObject(id);
 	InfoWindow iw;
 	iw.player = h->tempOwner;
 	iw.components.push_back(Component(4,subID,0,0));

+ 1 - 0
hch/CObjectHandler.h

@@ -221,6 +221,7 @@ public:
 
 	void initHero(); 
 	void initHero(int SUBID); 
+	void initHeroDefInfo();
 	CGHeroInstance();
 	virtual ~CGHeroInstance();
 

+ 2 - 2
lib/Connection.h

@@ -17,7 +17,7 @@
 #include <boost/mpl/identity.hpp>
 
 #include <boost/type_traits/is_array.hpp>
-const int version = 63;
+const ui32 version = 63;
 class CConnection;
 
 namespace mpl = boost::mpl;
@@ -189,7 +189,7 @@ public:
 	}
 	
 	template<class T>
-	COSer & operator&(T & t)
+	COSer & operator&(const T & t)
 	{
 		return * this->This() << t;
 	}	

+ 4 - 0
map.cpp

@@ -1418,6 +1418,7 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
 		case 32: // Garden of Revelation
 		case 100: //Learning Stone
 		case 102: //Tree of Knowledge
+		case 41: //Library of Enlightenment
 			{
 				nobj = new CGVisitableOPH();
 				break;
@@ -1584,6 +1585,9 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
 					{
 						art->army = readCreatureSet(bufor,i,7,(version>RoE));
 					}
+				}
+				if(areSettings || defInfo->id==93)
+				{
 					art->spell = readNormalNr(bufor,i); 
 					i+=4;
 				}

+ 4 - 1
map.h

@@ -229,6 +229,7 @@ public:
 class DLL_EXPORT CMapInfo : public CMapHeader
 {
 public:
+	ui8 seldiff; //selected difficulty (only in saved games)
 	std::string filename;
 	std::string date;
 	int playerAmnt, humenPlayers;
@@ -366,10 +367,12 @@ struct DLL_EXPORT Mapa : public CMapHeader
 		if(h.saving) //create vector with all defs used on map
 		{
 			for(int i=0; i<objects.size(); i++)
-				objects[i]->defInfo->serial = -1; //set serial to serial -1 - indicates that def is not present in defs vector
+				if(objects[i])
+					objects[i]->defInfo->serial = -1; //set serial to serial -1 - indicates that def is not present in defs vector
 
 			for(int i=0; i<objects.size(); i++)
 			{
+				if(!objects[i]) continue;
 				CGDefInfo *cur = objects[i]->defInfo;
 				if(cur->serial < 0)
 				{

+ 9 - 3
server/CGameHandler.cpp

@@ -413,18 +413,19 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 					std::string fname;
 					Mapa * mapa;
 					c >> fname;
-
 					{
+						sendMessageTo(c,"Serializing game info...");
 						CSaveFile save(std::string("Games") + PATHSEPARATOR + fname + ".vlgm1");
 						char hlp[8] = "VCMISVG";
-						save << hlp << static_cast<CMapHeader&>(*gs->map) << *VLC << gs;
+						save << hlp << version << static_cast<CMapHeader&>(*gs->map) << gs->scenarioOps->difficulty << *VLC << gs;
 					}
 
 					{
+						sendMessageTo(c,"Serializing server info...");
 						CSaveFile save(std::string("Games") + PATHSEPARATOR + fname + ".vsgm1");
 						save << *this;
 					}
-
+					sendMessageTo(c,"Game has been succesfully saved!");
 					break;
 				}
 			case 99: //end!
@@ -2342,4 +2343,9 @@ void CGameHandler::setObjProperty( int objid, int prop, int val )
 	sob.what = prop;
 	sob.val = val;
 	sendAndApply(&sob);
+}
+
+void CGameHandler::sendMessageTo( CConnection &c, std::string message )
+{
+	c << ui16(95) << message;
 }

+ 1 - 0
server/CGameHandler.h

@@ -62,6 +62,7 @@ class CGameHandler : public IGameCallback
 	PlayerStatuses states; //player color -> player state
 	std::set<CConnection*> conns;
 
+	void sendMessageTo(CConnection &c, std::string message);
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 	void moveStack(int stack, int dest);
 	void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero

+ 2 - 1
server/CVCMIServer.cpp

@@ -169,11 +169,12 @@ void CVCMIServer::loadGame( CConnection *c )
 	*c >> clients >> fname; //how many clients should be connected - TODO: support more than one
 
 	{
+		ui32 ver;
 		char sig[8];
 		CMapHeader dum;
 
 		CLoadFile lf(fname + ".vlgm1");
-		lf >> sig >> dum;
+		lf >> sig >> ver >> dum >> *sig;
 		tlog0 <<"Reading save signature"<<std::endl;
 
 		lf >> *VLC;