Преглед изворни кода

* saving system options
* saving hero direction
* minor changes in saving

Michał W. Urbańczyk пре 16 година
родитељ
комит
eb40ade906

+ 8 - 6
CGameInterface.cpp

@@ -24,23 +24,25 @@ CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname)
 	CGlobalAI*(*getAI)(); 
 	void(*getName)(char*); 
 
+	std::string dllPath;
+
 #ifdef _WIN32
-	dllname = "AI/"+dllname+".dll";
-	HINSTANCE dll = LoadLibraryA(dllname.c_str());
+	dllPath = "AI/"+dllname+".dll";
+	HINSTANCE dll = LoadLibraryA(dllPath.c_str());
 	if (!dll)
 	{
-		tlog1 << "Cannot open AI library ("<<dllname<<"). Throwing..."<<std::endl;
+		tlog1 << "Cannot open AI library ("<<dllPath<<"). Throwing..."<<std::endl;
 		throw new std::string("Cannot open AI library");
 	}
 	//int len = dllname.size()+1;
 	getName = (void(*)(char*))GetProcAddress(dll,"GetAiName");
 	getAI = (CGlobalAI*(*)())GetProcAddress(dll,"GetNewAI");
 #else
-	dllname = "AI/"+dllname+".so";
-	void *dll = dlopen(dllname.c_str(), RTLD_LOCAL | RTLD_LAZY);
+	dllPath = "AI/"+dllPath+".so";
+	void *dll = dlopen(dllPath.c_str(), RTLD_LOCAL | RTLD_LAZY);
 	if (!dll)
 	{
-		tlog1 << "Cannot open AI library ("<<dllname<<"). Throwing..."<<std::endl;
+		tlog1 << "Cannot open AI library ("<<dllPath<<"). Throwing..."<<std::endl;
 		throw new std::string("Cannot open AI library");
 	}
 	getName = (void(*)(char*))dlsym(dll,"GetAiName");

+ 1 - 1
client/CAdvmapInterface.cpp

@@ -1462,7 +1462,7 @@ void CAdvMapInt::show(SDL_Surface *to)
 	++heroAnim;
 
 	//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
-	if((animValHitCount % (4/LOCPLINT->mapScrollingSpeed)) == 0 
+	if((animValHitCount % (4/LOCPLINT->sysOpts.mapScrollingSpeed)) == 0 
 		&& 
 			(LOCPLINT->topInt() == this)
 			|| SDL_GetKeyState(NULL)[SDLK_LCTRL] 

+ 21 - 2
client/CMT.cpp

@@ -62,6 +62,7 @@ SDL_Surface *screen = NULL, //main screen surface
 	*screen2 = NULL,//and hlp surface (used to store not-active interfaces layer) 
 	*screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
 
+SystemOptions GDefaultOptions; 
 std::queue<SDL_Event*> events;
 boost::mutex eventsM;
 
@@ -97,15 +98,33 @@ void init()
 	}
 	THC tlog0<<"\tInitializing fonts: "<<pomtime.getDif()<<std::endl;
 
+	{
+		//read system options
+		CLoadFile settings("config" PATHSEPARATOR "sysopts.bin");
+		if(settings.sfile)
+		{
+			settings >> GDefaultOptions;
+		}
+		else //file not found (probably, may be also some kind of access problem
+		{
+			tlog2 << "Warning: Cannot read system options, default settings will be used.\n";
+			
+			//Try to create file
+			tlog2 << "VCMI will try to save default system options...\n";
+			GDefaultOptions.settingsChanged();
+		}
+	}
+	THC tlog0<<"\tLoading default system settings: "<<pomtime.getDif()<<std::endl;
+
 	//initializing audio
 	// Note: because of interface button range, volume can only be a
 	// multiple of 11, from 0 to 99.
 	CGI->soundh = new CSoundHandler;
 	CGI->soundh->init();
-	CGI->soundh->setVolume(88);
+	CGI->soundh->setVolume(GDefaultOptions.soundVolume);
 	CGI->musich = new CMusicHandler;
 	CGI->musich->init();
-	CGI->musich->setVolume(88);
+	CGI->musich->setVolume(GDefaultOptions.musicVolume);
 	tlog0<<"\tInitializing sound: "<<pomtime.getDif()<<std::endl;
 	tlog0<<"Initializing screen, fonts and sound handling: "<<tmh.getDif()<<std::endl;
 

+ 1 - 1
client/CMessage.cpp

@@ -390,7 +390,7 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, int player, int
 	amin(txts.first, conf.cc.resx - 150);
 	amin(txts.second, conf.cc.resy - 150);
 
-	ret->bitmap = drawBox1(txts.first+2*SIDE_MARGIN,txts.second+2*SIDE_MARGIN,0);
+	ret->bitmap = drawBox1(txts.first+2*SIDE_MARGIN,txts.second+2*SIDE_MARGIN,player);
 	ret->pos.h=ret->bitmap->h;
 	ret->pos.w=ret->bitmap->w;
 	ret->pos.x=screen->w/2-(ret->pos.w/2);

+ 55 - 50
client/CPlayerInterface.cpp

@@ -91,8 +91,7 @@ CPlayerInterface::CPlayerInterface(int Player, int serial)
 	pim = new boost::recursive_mutex;
 	makingTurn = false;
 	showingDialog = new CondSh<bool>(false);
-	heroMoveSpeed = 2;
-	mapScrollingSpeed = 2;
+	sysOpts = GDefaultOptions;
 	//initializing framerate keeper
 	mainFPSmng = new FPSmanager;
 	SDL_initFramerate(mainFPSmng);
@@ -267,43 +266,6 @@ inline void delObjRect(const int & x, const int & y, const int & z, const int &
 			return;
 		}
 }
-static int getDir(int3 src, int3 dst)
-{
-	int ret = -1;
-	if(dst.x+1 == src.x && dst.y+1 == src.y) //tl
-	{
-		ret = 1;
-	}
-	else if(dst.x == src.x && dst.y+1 == src.y) //t
-	{
-		ret = 2;
-	}
-	else if(dst.x-1 == src.x && dst.y+1 == src.y) //tr
-	{
-		ret = 3;
-	}
-	else if(dst.x-1 == src.x && dst.y == src.y) //r
-	{
-		ret = 4;
-	}
-	else if(dst.x-1 == src.x && dst.y-1 == src.y) //br
-	{
-		ret = 5;
-	}
-	else if(dst.x == src.x && dst.y-1 == src.y) //b
-	{
-		ret = 6;
-	}
-	else if(dst.x+1 == src.x && dst.y-1 == src.y) //bl
-	{
-		ret = 7;
-	}
-	else if(dst.x+1 == src.x && dst.y == src.y) //l
-	{
-		ret = 8;
-	}
-	return ret;
-}
 void CPlayerInterface::heroMoved(const TryMoveHero & details)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
@@ -322,7 +284,6 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	{
 		if(details.result == TryMoveHero::BLOCKING_VISIT)
 		{
-			ho->moveDir = getDir(details.start,details.end);
 			adventureInt->paths.erase(ho);
 			adventureInt->terrain.currentPath = NULL;
 		}
@@ -346,7 +307,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 
 	if(details.end.x+1 == details.start.x && details.end.y+1 == details.start.y) //tl
 	{
-		ho->moveDir = 1;
+		//ho->moveDir = 1;
 		ho->isStanding = false;
 		CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, -31)));
 		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 1, -31)));
@@ -374,7 +335,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 	else if(details.end.x == details.start.x && details.end.y+1 == details.start.y) //t
 	{
-		ho->moveDir = 2;
+		//ho->moveDir = 2;
 		ho->isStanding = false;
 		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 0, -31)));
 		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 32, -31)));
@@ -394,7 +355,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 	else if(details.end.x-1 == details.start.x && details.end.y+1 == details.start.y) //tr
 	{
-		ho->moveDir = 3;
+		//ho->moveDir = 3;
 		ho->isStanding = false;
 		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -1, -31)));
 		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 31, -31)));
@@ -422,7 +383,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 	else if(details.end.x-1 == details.start.x && details.end.y == details.start.y) //r
 	{
-		ho->moveDir = 4;
+		//ho->moveDir = 4;
 		ho->isStanding = false;
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 0), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 0), ho->id);
@@ -440,7 +401,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 	else if(details.end.x-1 == details.start.x && details.end.y-1 == details.start.y) //br
 	{
-		ho->moveDir = 5;
+		//ho->moveDir = 5;
 		ho->isStanding = false;
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, -1), ho->id);
@@ -468,7 +429,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 	else if(details.end.x == details.start.x && details.end.y-1 == details.start.y) //b
 	{
-		ho->moveDir = 6;
+		//ho->moveDir = 6;
 		ho->isStanding = false;
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 0, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 32, -1), ho->id);
@@ -488,7 +449,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 	else if(details.end.x+1 == details.start.x && details.end.y-1 == details.start.y) //bl
 	{
-		ho->moveDir = 7;
+		//ho->moveDir = 7;
 		ho->isStanding = false;
 		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, -1)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, -1), ho->id);
@@ -516,7 +477,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 	else if(details.end.x+1 == details.start.x && details.end.y == details.start.y) //l
 	{
-		ho->moveDir = 8;
+		//ho->moveDir = 8;
 		ho->isStanding = false;
 		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, 0)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 0), ho->id);
@@ -535,7 +496,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	//first initializing done
 	SDL_framerateDelay(mainFPSmng); // after first move
 	//main moving
-	for(int i=1; i<32; i+=2*heroMoveSpeed)
+	for(int i=1; i<32; i+=2*sysOpts.heroMoveSpeed)
 	{
 		if(details.end.x+1 == details.start.x && details.end.y+1 == details.start.y) //tl
 		{
@@ -1568,7 +1529,7 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const HeroB
 template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, const int version )
 {
 	h & playerID & serialID;
-	h & heroMoveSpeed & mapScrollingSpeed;
+	h & sysOpts;
 	h & CBattleInterface::settings;
 }
 
@@ -1580,6 +1541,7 @@ void CPlayerInterface::serialize( COSer<CSaveFile> &h, const int version )
 void CPlayerInterface::serialize( CISer<CLoadFile> &h, const int version )
 {
 	serializeTempl(h,version);
+	sysOpts.apply();
 }
 
 void CPlayerInterface::redrawHeroWin(const CGHeroInstance * hero)
@@ -1774,4 +1736,47 @@ void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, int lev
 	}
 	CRecruitmentWindow *cr = new CRecruitmentWindow(cres, boost::bind(&CCallback::recruitCreatures, cb, dwelling, _1, _2));
 	pushInt(cr);
+}
+
+void SystemOptions::setMusicVolume( int newVolume )
+{
+	musicVolume = newVolume;
+	CGI->musich->setVolume(newVolume);
+	settingsChanged();
+}
+
+void SystemOptions::setSoundVolume( int newVolume )
+{
+	soundVolume = newVolume;
+	CGI->soundh->setVolume(newVolume);
+	settingsChanged();
+}
+
+void SystemOptions::setHeroMoveSpeed( int newSpeed )
+{
+	heroMoveSpeed = newSpeed;
+	settingsChanged();
+}
+
+void SystemOptions::setMapScrollingSpeed( int newSpeed )
+{
+	mapScrollingSpeed = newSpeed;
+	settingsChanged();
+}
+
+void SystemOptions::settingsChanged()
+{
+	CSaveFile settings("config" PATHSEPARATOR "sysopts.bin");
+
+	if(settings.sfile)
+		settings << *this;
+	else
+		tlog1 << "Cannot save settings to config" PATHSEPARATOR "sysopts.bin!\n";
+}
+
+void SystemOptions::apply()
+{
+	CGI->musich->setVolume(musicVolume);
+	CGI->soundh->setVolume(soundVolume);
+	settingsChanged();
 }

+ 33 - 5
client/CPlayerInterface.h

@@ -71,6 +71,38 @@ namespace boost
 	class recursive_mutex;
 };
 
+struct SystemOptions
+{
+	ui8 heroMoveSpeed; //speed of player's hero movement
+	//TODO: enemy hero speed
+	ui8 mapScrollingSpeed; //map scrolling speed
+	ui8 musicVolume, soundVolume;
+
+	//TODO: rest of system options
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & heroMoveSpeed & mapScrollingSpeed & musicVolume & soundVolume;
+	}
+
+	SystemOptions()
+	{
+		heroMoveSpeed = 2;
+		mapScrollingSpeed = 2;
+		musicVolume = 88;
+		soundVolume = 88;
+	}
+
+	void setHeroMoveSpeed(int newSpeed); //set for the member above
+	void setMapScrollingSpeed(int newSpeed); //set the member above
+	void setMusicVolume(int newVolume);
+	void setSoundVolume(int newVolume);
+	void settingsChanged(); //updates file with "default" settings for next running of application
+	void apply();
+};
+
+extern SystemOptions GDefaultOptions; //defined and inited in CMT.cpp, stores default settings loaded with application
+
 class CPlayerInterface : public CGameInterface
 {
 public:
@@ -80,11 +112,7 @@ public:
 	boost::recursive_mutex *pim;
 	bool makingTurn; //if player is already making his turn
 
-	//TODO: exclude to some kind Settings struct
-	int heroMoveSpeed; //speed of player's hero movement
-	void setHeroMoveSpeed(int newSpeed) {heroMoveSpeed = newSpeed;} //set for the member above
-	int mapScrollingSpeed; //map scrolling speed
-	void setMapScrollingSpeed(int newSpeed) {mapScrollingSpeed = newSpeed;} //set the member above
+	SystemOptions sysOpts;
 
 	SDL_Event * current; //current event
 	CAdvMapInt * adventureInt;

+ 32 - 22
client/Client.cpp

@@ -257,23 +257,27 @@ void CClient::load( const std::string & fname )
 	*serv << ui8(255); // neutrals
 	tlog0 <<"Sent info to server: "<<tmh.getDif()<<std::endl;
 	
-	for (size_t i=0; i<gs->scenarioOps->playerInfos.size();++i) //initializing interfaces for players
-	{ 
-		ui8 color = gs->scenarioOps->playerInfos[i].color;
-		CCallback *cb = new CCallback(gs,color,this);
-		if(!gs->scenarioOps->playerInfos[i].human) {
-			playerint[color] = static_cast<CGameInterface*>(CAIHandler::getNewAI(cb,conf.cc.defaultAI));
-		}
-		else {
-			playerint[color] = new CPlayerInterface(color,i);
-		}
-		gs->currentPlayer = color;
-		playerint[color]->init(cb);
-		tlog0 <<"Setting up interface for player "<< (int)color <<": "<<tmh.getDif()<<std::endl;
+	{
+		CLoadFile lf(fname + ".vcgm1");
+		lf >> *this;
 	}
-	playerint[255] =  CAIHandler::getNewAI(cb,conf.cc.defaultAI);
-	playerint[255]->init(new CCallback(gs,255,this));
-	tlog0 <<"Setting up interface for neutral \"player\"" << tmh.getDif() << std::endl;
+	//for (size_t i=0; i<gs->scenarioOps->playerInfos.size();++i) //initializing interfaces for players
+	//{ 
+	//	ui8 color = gs->scenarioOps->playerInfos[i].color;
+	//	CCallback *cb = new CCallback(gs,color,this);
+	//	if(!gs->scenarioOps->playerInfos[i].human) {
+	//		playerint[color] = static_cast<CGameInterface*>(CAIHandler::getNewAI(cb,conf.cc.defaultAI));
+	//	}
+	//	else {
+	//		playerint[color] = new CPlayerInterface(color,i);
+	//	}
+	//	gs->currentPlayer = color;
+	//	playerint[color]->init(cb);
+	//	tlog0 <<"Setting up interface for player "<< (int)color <<": "<<tmh.getDif()<<std::endl;
+	//}
+	//playerint[255] =  CAIHandler::getNewAI(cb,conf.cc.defaultAI);
+	//playerint[255]->init(new CCallback(gs,255,this));
+	//tlog0 <<"Setting up interface for neutral \"player\"" << tmh.getDif() << std::endl;
 
 
 }
@@ -397,13 +401,19 @@ void CClient::serialize( Handler &h, const int version )
 			ui8 pid;
 			h & pid & dllname;
 
+			CCallback *callback = new CCallback(gs,pid,this);
+			callbacks.insert(callback);
+			CGameInterface *nInt = NULL;
+
+
 			if(dllname.length())
-			{
-				CCallback *callback = new CCallback(gs,pid,this);
-				callbacks.insert(callback);
-				playerint[pid] =  CAIHandler::getNewAI(callback,dllname);
-				playerint[pid]->init(callback);
-			}
+				nInt = CAIHandler::getNewAI(callback,dllname);
+			else
+				nInt = new CPlayerInterface(pid,i);
+
+			playerint[pid] = nInt;
+			nInt->init(callback);
+			nInt->serialize(h, version);
 		}
 	}
 }

+ 6 - 6
client/GUIClasses.cpp

@@ -2676,15 +2676,15 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[350].first),CGI->generaltexth->zelp[350].second, "sysopb2.def", 235, 134, 2);
 	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[351].first),CGI->generaltexth->zelp[351].second, "sysopb3.def", 283, 134, 4);
 	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[352].first),CGI->generaltexth->zelp[352].second, "sysopb4.def", 331, 134, 8);
-	heroMoveSpeed->select(owner->heroMoveSpeed, 1);
-	heroMoveSpeed->onChange = boost::bind(&CPlayerInterface::setHeroMoveSpeed, owner, _1);
+	heroMoveSpeed->select(owner->sysOpts.heroMoveSpeed, 1);
+	heroMoveSpeed->onChange = boost::bind(&SystemOptions::setHeroMoveSpeed, owner->sysOpts, _1);
 
 	mapScrollSpeed = new CHighlightableButtonsGroup(0);
 	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[357].first),CGI->generaltexth->zelp[357].second, "sysopb9.def", 187, 267, 1);
 	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[358].first),CGI->generaltexth->zelp[358].second, "sysob10.def", 251, 267, 2);
 	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[359].first),CGI->generaltexth->zelp[359].second, "sysob11.def", 315, 267, 4);
-	mapScrollSpeed->select(owner->mapScrollingSpeed, 1);
-	mapScrollSpeed->onChange = boost::bind(&CPlayerInterface::setMapScrollingSpeed, owner, _1);
+	mapScrollSpeed->select(owner->sysOpts.mapScrollingSpeed, 1);
+	mapScrollSpeed->onChange = boost::bind(&SystemOptions::setMapScrollingSpeed, owner->sysOpts, _1);
 
 	musicVolume = new CHighlightableButtonsGroup(0, true);
 	for(int i=0; i<10; ++i)
@@ -2692,7 +2692,7 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 		musicVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[326+i].first),CGI->generaltexth->zelp[326+i].second, "syslb.def", 188 + 19*i, 416, i*11);
 	}
 	musicVolume->select(CGI->musich->getVolume(), 1);
-	musicVolume->onChange = boost::bind(&CMusicHandler::setVolume, CGI->musich, _1);
+	musicVolume->onChange = boost::bind(&SystemOptions::setMusicVolume, owner->sysOpts, _1);
 
 	effectsVolume = new CHighlightableButtonsGroup(0, true);
 	for(int i=0; i<10; ++i)
@@ -2700,7 +2700,7 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 		effectsVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[336+i].first),CGI->generaltexth->zelp[336+i].second, "syslb.def", 188 + 19*i, 482, i*11);
 	}
 	effectsVolume->select(CGI->soundh->getVolume(), 1);
-	effectsVolume->onChange = boost::bind(&CSoundHandler::setVolume, CGI->soundh, _1);
+	effectsVolume->onChange = boost::bind(&SystemOptions::setSoundVolume, owner->sysOpts, _1);
 }
 
 CSystemOptionsWindow::~CSystemOptionsWindow()

+ 3 - 2
hch/CObjectHandler.h

@@ -182,7 +182,7 @@ class DLL_EXPORT CGHeroInstance : public CArmedInstance
 public:
 	//////////////////////////////////////////////////////////////////////////
 
-	mutable int moveDir; //format:	123
+	ui8 moveDir; //format:	123
 					//		8 4
 					//		765
 	mutable ui8 isStanding, tacticFormationEnabled;
@@ -226,7 +226,8 @@ public:
 	{
 		h & static_cast<CArmedInstance&>(*this);
 		h & exp & level & name & biography & portrait & mana & primSkills & secSkills & movement
-			& identifier & sex & inTownGarrison & artifacts & artifWorn & spells & patrol & bonuses;
+			& identifier & sex & inTownGarrison & artifacts & artifWorn & spells & patrol & bonuses
+			& moveDir;
 
 		ui8 standardType = (VLC->heroh->heroes[subID] == type);
 		h & standardType;

+ 42 - 0
lib/NetPacksLib.cpp

@@ -245,6 +245,44 @@ DLL_EXPORT void RemoveObject::applyGs( CGameState *gs )
 	}
 }
 
+static int getDir(int3 src, int3 dst)
+{
+	int ret = -1;
+	if(dst.x+1 == src.x && dst.y+1 == src.y) //tl
+	{
+		ret = 1;
+	}
+	else if(dst.x == src.x && dst.y+1 == src.y) //t
+	{
+		ret = 2;
+	}
+	else if(dst.x-1 == src.x && dst.y+1 == src.y) //tr
+	{
+		ret = 3;
+	}
+	else if(dst.x-1 == src.x && dst.y == src.y) //r
+	{
+		ret = 4;
+	}
+	else if(dst.x-1 == src.x && dst.y-1 == src.y) //br
+	{
+		ret = 5;
+	}
+	else if(dst.x == src.x && dst.y-1 == src.y) //b
+	{
+		ret = 6;
+	}
+	else if(dst.x+1 == src.x && dst.y-1 == src.y) //bl
+	{
+		ret = 7;
+	}
+	else if(dst.x+1 == src.x && dst.y == src.y) //l
+	{
+		ret = 8;
+	}
+	return ret;
+}
+
 void TryMoveHero::applyGs( CGameState *gs )
 {
 	CGHeroInstance *h = gs->getHero(id);
@@ -255,8 +293,12 @@ void TryMoveHero::applyGs( CGameState *gs )
 		h->pos = end;
 		gs->map->addBlockVisTiles(h);
 	}
+
 	BOOST_FOREACH(int3 t, fowRevealed)
 		gs->getPlayer(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1;
+
+	if(result == SUCCESS  ||  result == BLOCKING_VISIT)
+		h->moveDir = getDir(start,end);
 }
 
 DLL_EXPORT void SetGarrisons::applyGs( CGameState *gs )

+ 2 - 2
server/CGameHandler.cpp

@@ -1500,8 +1500,8 @@ void CGameHandler::save( const std::string &fname )
 	{
 		tlog0 << "Ordering clients to serialize...\n";
 		SaveGame sg(fname);
-		//TODO: uncomment when client saving is ready
-		//sendToAllClients(&sg);
+
+		sendToAllClients(&sg);
 	}
 
 	{