Browse Source

Fixes for adventure AI: battles where AI is the attacker should work, fixed drawing AI heroes under FoW.

Restructured thread structure: no new thread on yourturn, instead of that introduced update() method called by thread dispatching GUI events. 

Further changes are planned.
Michał W. Urbańczyk 16 years ago
parent
commit
01831e912a

+ 2 - 2
AI/GeniusAI/BattleLogic.cpp

@@ -39,7 +39,7 @@ ui8 side; //who made this action: false - left, true - right player
  */
 CBattleLogic::CBattleLogic(ICallback *cb,  CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side) :
 	m_iCurrentTurn(-2),
-	m_bIsAttacker(false),
+	m_bIsAttacker(!side),
 	m_cb(cb),
 	m_army1(army1),
 	m_army2(army2),
@@ -261,7 +261,7 @@ BattleAction CBattleLogic::MakeDecision(int stackID)
 	MakeStatistics(stackID);
 
 	list<int> creatures;
-	int additionalInfo;
+	int additionalInfo = 0; //?
 
 	if (m_bEnemyDominates)
 	{

+ 105 - 105
client/CAdvmapInterface.cpp

@@ -710,138 +710,140 @@ void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 	int turns = pnode->turns;
 	amin(turns, 3);
 
-	if(LOCPLINT->adventureInt->selection->ID == TOWNI_TYPE)
+	if(LOCPLINT->adventureInt->selection)
 	{
-		if(obj)
+		if(LOCPLINT->adventureInt->selection->ID == TOWNI_TYPE)
 		{
-			if(obj->ID == TOWNI_TYPE)
+			if(obj)
 			{
-				CGI->curh->changeGraphic(0, 3);
+				if(obj->ID == TOWNI_TYPE)
+				{
+					CGI->curh->changeGraphic(0, 3);
+				}
+				else if(obj->ID == HEROI_TYPE)
+				{
+					CGI->curh->changeGraphic(0, 2);
+				}
 			}
-			else if(obj->ID == HEROI_TYPE)
+			else
 			{
-				CGI->curh->changeGraphic(0, 2);
+				CGI->curh->changeGraphic(0, 0);
 			}
 		}
-		else
-		{
-			CGI->curh->changeGraphic(0, 0);
-		}
-	}
-	else if(LOCPLINT->adventureInt->selection->ID == HEROI_TYPE)
-	{
-		const CGHeroInstance *h = static_cast<const CGHeroInstance *>(LOCPLINT->adventureInt->selection);
-		if(obj)
+		else if(LOCPLINT->adventureInt->selection->ID == HEROI_TYPE)
 		{
-			if(obj->ID == HEROI_TYPE)
+			const CGHeroInstance *h = static_cast<const CGHeroInstance *>(LOCPLINT->adventureInt->selection);
+			if(obj)
 			{
-				if(obj->tempOwner != LOCPLINT->playerID) //enemy hero TODO: allies
+				if(obj->ID == HEROI_TYPE)
+				{
+					if(obj->tempOwner != LOCPLINT->playerID) //enemy hero TODO: allies
+					{
+						if(accessible)
+							CGI->curh->changeGraphic(0, 5 + turns*6);
+						else
+							CGI->curh->changeGraphic(0, 0);
+					}
+					else //our hero
+					{
+						if(LOCPLINT->adventureInt->selection == obj)
+							CGI->curh->changeGraphic(0, 2);
+						else if(accessible)
+							CGI->curh->changeGraphic(0, 8 + turns*6);
+						else
+							CGI->curh->changeGraphic(0, 2);
+					}
+				}
+				else if(obj->ID == TOWNI_TYPE)
+				{
+					if(obj->tempOwner != LOCPLINT->playerID) //enemy town TODO: allies
+					{
+						if(accessible) {
+							const CGTownInstance* townObj = dynamic_cast<const CGTownInstance*>(obj);
+
+							// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
+							if (townObj && townObj->army.slots.empty())
+								CGI->curh->changeGraphic(0, 9 + turns*6);
+							else
+								CGI->curh->changeGraphic(0, 5 + turns*6);
+								
+						} else {
+							CGI->curh->changeGraphic(0, 0);
+						}
+					}
+					else //our town
+					{
+						if(accessible)
+							CGI->curh->changeGraphic(0, 9 + turns*6);
+						else
+							CGI->curh->changeGraphic(0, 3);
+					}
+				}
+				else if(obj->ID == 54) //monster
 				{
 					if(accessible)
 						CGI->curh->changeGraphic(0, 5 + turns*6);
 					else
 						CGI->curh->changeGraphic(0, 0);
 				}
-				else //our hero
+				else if(obj->ID == 8) //boat
 				{
-					if(LOCPLINT->adventureInt->selection == obj)
-						CGI->curh->changeGraphic(0, 2);
-					else if(accessible)
-						CGI->curh->changeGraphic(0, 8 + turns*6);
+					if(accessible)
+						CGI->curh->changeGraphic(0, 6 + turns*6);
 					else
-						CGI->curh->changeGraphic(0, 2);
+						CGI->curh->changeGraphic(0, 0);
 				}
-			}
-			else if(obj->ID == TOWNI_TYPE)
-			{
-				if(obj->tempOwner != LOCPLINT->playerID) //enemy town TODO: allies
+				else if (obj->ID == 33 || obj->ID == 219) // Garrison
 				{
-					if(accessible) {
-						const CGTownInstance* townObj = dynamic_cast<const CGTownInstance*>(obj);
+					if (accessible) {
+						const CGGarrison* garrObj = dynamic_cast<const CGGarrison*>(obj);
 
-						// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
-						if (townObj && townObj->army.slots.empty())
-							CGI->curh->changeGraphic(0, 9 + turns*6);
-						else
+						// Show battle cursor for guarded enemy garrisons, otherwise movement cursor.
+						if (garrObj && garrObj->tempOwner != LOCPLINT->playerID
+							&& !garrObj->army.slots.empty())
+						{
 							CGI->curh->changeGraphic(0, 5 + turns*6);
-							
+						}
+						else
+						{
+							CGI->curh->changeGraphic(0, 9 + turns*6);
+						}
 					} else {
 						CGI->curh->changeGraphic(0, 0);
 					}
 				}
-				else //our town
+				else
 				{
 					if(accessible)
-						CGI->curh->changeGraphic(0, 9 + turns*6);
-					else
-						CGI->curh->changeGraphic(0, 3);
-				}
-			}
-			else if(obj->ID == 54) //monster
-			{
-				if(accessible)
-					CGI->curh->changeGraphic(0, 5 + turns*6);
-				else
-					CGI->curh->changeGraphic(0, 0);
-			}
-			else if(obj->ID == 8) //boat
-			{
-				if(accessible)
-					CGI->curh->changeGraphic(0, 6 + turns*6);
-				else
-					CGI->curh->changeGraphic(0, 0);
-			}
-			else if (obj->ID == 33 || obj->ID == 219) // Garrison
-			{
-				if (accessible) {
-					const CGGarrison* garrObj = dynamic_cast<const CGGarrison*>(obj);
-
-					// Show battle cursor for guarded enemy garrisons, otherwise movement cursor.
-					if (garrObj && garrObj->tempOwner != LOCPLINT->playerID
-						&& !garrObj->army.slots.empty())
 					{
-						CGI->curh->changeGraphic(0, 5 + turns*6);
+						if(pnode->land)
+							CGI->curh->changeGraphic(0, 9 + turns*6);
+						else
+							CGI->curh->changeGraphic(0, 28 + turns);
 					}
 					else
-					{
-						CGI->curh->changeGraphic(0, 9 + turns*6);
-					}
-				} else {
-					CGI->curh->changeGraphic(0, 0);
+						CGI->curh->changeGraphic(0, 0);
 				}
-			}
-			else
+			} 
+			else //no objs 
 			{
 				if(accessible)
 				{
 					if(pnode->land)
-						CGI->curh->changeGraphic(0, 9 + turns*6);
+					{
+						if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water)
+							CGI->curh->changeGraphic(0, 4 + turns*6);
+						else
+							CGI->curh->changeGraphic(0, 7 + turns*6); //anchor
+					}
 					else
-						CGI->curh->changeGraphic(0, 28 + turns);
+						CGI->curh->changeGraphic(0, 6 + turns*6);
 				}
 				else
 					CGI->curh->changeGraphic(0, 0);
 			}
-		} 
-		else //no objs 
-		{
-			if(accessible)
-			{
-				if(pnode->land)
-				{
-					if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water)
-						CGI->curh->changeGraphic(0, 4 + turns*6);
-					else
-						CGI->curh->changeGraphic(0, 7 + turns*6); //anchor
-				}
-				else
-					CGI->curh->changeGraphic(0, 6 + turns*6);
-			}
-			else
-				CGI->curh->changeGraphic(0, 0);
 		}
 	}
-
 	//tlog1 << "Tile " << pom << ": Turns=" << (int)pnode->turns <<"  Move:=" << pnode->moveRemains <</* " (from  "  << ")" << */std::endl;
 }
 void CTerrainRect::hover(bool on)
@@ -1515,7 +1517,6 @@ townList(ADVOPT.tlistSize,ADVOPT.tlistX,ADVOPT.tlistY,ADVOPT.tlistAU,ADVOPT.tlis
 	pos.x = pos.y = 0;
 	pos.w = screen->w;
 	pos.h = screen->h;
-	active = 0;
 	selection = NULL;
 	townList.fun = boost::bind(&CAdvMapInt::selectionChanged,this);
 	LOCPLINT->adventureInt=this;
@@ -1631,14 +1632,14 @@ void CAdvMapInt::fnextHero()
 void CAdvMapInt::fendTurn()
 {
 	LOCPLINT->makingTurn = false;
+	LOCPLINT->cb->endTurn();
 }
 
 void CAdvMapInt::activate()
 {
-	if(active++)
+	if(isActive())
 	{
 		tlog1 << "Error: advmapint already active...\n";
-		active--;
 		return;
 	}
 	screenBuf = screen;
@@ -1689,12 +1690,6 @@ void CAdvMapInt::deactivate()
 	infoBar.mode=-1;
 
 	LOCPLINT->cingconsole->deactivate();
-
-	if(--active)
-	{
-		tlog1 << "Error: advmapint still active...\n";
-		deactivate();
-	}
 }
 void CAdvMapInt::showAll(SDL_Surface *to)
 {
@@ -1807,16 +1802,16 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 	switch(k)
 	{
 	case SDLK_i: 
-		if(active)
+		if(isActive())
 			CAdventureOptions::showScenarioInfo();
 		return;
 	case SDLK_s: 
-		if(active)
+		if(isActive())
 			GH.pushInt(new CSelectionScreen(saveGame));
 		return;
 	case SDLK_SPACE: //space - try to revisit current object with selected hero
 		{
-			if(!active) 
+			if(!isActive()) 
 				return;
 			const CGHeroInstance *h = dynamic_cast<const CGHeroInstance*>(selection);
 			if(h && key.state == SDL_PRESSED)
@@ -1829,7 +1824,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 		return;
 	case SDLK_RETURN:
 		{
-			if(!active || !selection || key.state != SDL_PRESSED) 
+			if(!isActive() || !selection || key.state != SDL_PRESSED) 
 				return;
 			if(selection->ID == HEROI_TYPE)
 				LOCPLINT->openHeroWindow(static_cast<const CGHeroInstance*>(selection));
@@ -1883,7 +1878,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 				k = arrowToNum(SDLKey(k));
 			}
 
-			if(!active || LOCPLINT->ctrlPressed())//ctrl makes arrow move screen, not hero
+			if(!isActive() || LOCPLINT->ctrlPressed())//ctrl makes arrow move screen, not hero
 				break;
 
 			k -= SDLK_KP0 + 1;
@@ -2003,7 +1998,7 @@ void CAdvMapInt::select(const CArmedInstance *sel )
 void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
 {
 	//adventure map scrolling with mouse
-	if(!SDL_GetKeyState(NULL)[SDLK_LCTRL]  &&  active)
+	if(!SDL_GetKeyState(NULL)[SDLK_LCTRL]  &&  isActive())
 	{
 		if(sEvent.x<15)
 		{
@@ -2040,6 +2035,11 @@ void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
 	}
 }
 
+bool CAdvMapInt::isActive()
+{
+	return active & ~CIntObject.KEYBOARD;
+}
+
 CAdventureOptions::CAdventureOptions()
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;

+ 2 - 1
client/CAdvmapInterface.h

@@ -138,7 +138,7 @@ public:
 	~CAdvMapInt();
 
 	int3 position; //top left corner of visible map part
-	int player, active;
+	int player;
 
 
 	enum{LEFT=1, RIGHT=2, UP=4, DOWN=8};
@@ -202,5 +202,6 @@ public:
 	void handleRightClick(std::string text, tribool down, CIntObject * client);
 	void keyPressed(const SDL_KeyboardEvent & key);
 	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
+	bool isActive();
 };
 #endif // __CADVMAPINTERFACE_H__

+ 2 - 2
client/CBattleInterface.cpp

@@ -2327,13 +2327,13 @@ void CBattleInterface::hexLclicked(int whichOne)
 					giveCommand(9,whichOne,activeStack);
 				}
 			}
-			else if(dest->owner != attackingHeroInstance->tempOwner
+			else if(dest->owner != actSt->owner
 				&& LOCPLINT->cb->battleCanShoot(activeStack, whichOne) ) //shooting
 			{
 				CGI->curh->changeGraphic(1, 6); //cursor should be changed
 				giveCommand(7,whichOne,activeStack);
 			}
-			else if(dest->owner != attackingHeroInstance->tempOwner) //attacking
+			else if(dest->owner != actSt->owner) //attacking
 			{
 				const CStack * actStack = LOCPLINT->cb->battleGetStackByID(activeStack);
 				int attackFromHex = -1; //hex from which we will attack chosen stack

+ 18 - 18
client/CMT.cpp

@@ -69,7 +69,7 @@ static CClient *client;
 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
-static boost::thread *hhh;
+static boost::thread *mainGUIThread;
 
 SystemOptions GDefaultOptions; 
 VCMIDirs GVCMIDirs;
@@ -214,8 +214,8 @@ int main(int argc, char** argv)
 
 	CGI->musich->playMusic(musicBase::mainMenu, -1);
 
-	new CGPreGame; //will set CGP pointer to itself
-	hhh = new boost::thread(&CGPreGame::run, CGP);
+	GH.curInt = new CGPreGame; //will set CGP pointer to itself
+	mainGUIThread = new boost::thread(&CGuiHandler::run, boost::ref(GH));
 	listenForEvents();
 
 	return 0;
@@ -472,18 +472,18 @@ static void listenForEvents()
 		{
 			if (client)
 				client->stop();
-			if (hhh) {
-				CGP->terminate = true;
-				hhh->join();
-				delete hhh;
-				hhh = NULL;
+			if (mainGUIThread) 
+			{
+				GH.terminate = true;
+				mainGUIThread->join();
+				delete mainGUIThread;
+				mainGUIThread = NULL;
 			}
 			delete console;
 			console = NULL;
 			SDL_Delay(750);
 			SDL_Quit();
 			tlog0 << "Ending...\n";
-
 			break;
 		}
 		else if(LOCPLINT && ev->type == SDL_KEYDOWN && ev->key.keysym.sym==SDLK_F4)
@@ -501,9 +501,12 @@ static void listenForEvents()
 			delete ev;
 			continue;
 		}
-		else if (ev->type == SDL_USEREVENT && ev->user.code == 2) {
+		else if (ev->type == SDL_USEREVENT && ev->user.code == 2) 
+		{
 			client->stop();
 			delete ev;
+
+			GH.curInt = CGP;
 			continue;
 		}
 
@@ -517,6 +520,7 @@ static void listenForEvents()
 
 void startGame(StartInfo * options) 
 {
+	GH.curInt =NULL;
 	if(gOnlyAI)
 	{
 		for (size_t i =0; i < options->playerInfos.size(); i++)
@@ -534,22 +538,18 @@ void startGame(StartInfo * options)
 		SDL_PushEvent(&ev);
 	}
 
-	CClient cl;
+	client = new CClient;
 	if(options->mode == 0) //new game
 	{
-		cl.newGame(NULL, options);
+		client->newGame(NULL, options);
 	}
 	else //load game
 	{
 		std::string fname = options->mapname;
 		boost::algorithm::erase_last(fname,".vlgm1");
-		cl.loadGame(fname);
+		client->loadGame(fname);
 	}
 
-	client = &cl;
 	CGI->musich->stopMusic();
-	client->run();
-	LOCPLINT->terminate_cond.waitUntil(true);
-	client->endGame();
-	client = NULL;
+	client->connectionHandler = new boost::thread(&CClient::run, client);
 }

+ 108 - 75
client/CPlayerInterface.cpp

@@ -40,6 +40,8 @@
 #include <cmath>
 #include <queue>
 #include <sstream>
+#include <boost/filesystem.hpp>
+
 #ifdef min
 #undef min
 #endif
@@ -102,12 +104,16 @@ CPlayerInterface::CPlayerInterface(int Player, int serial)
 	cingconsole = new CInGameConsole;
 	terminate = false;
 	terminate_cond.set(false);
+	firstCall = 1; //if loading will be overwritten in serialize
+	autosaveCount = 0;
 }
 CPlayerInterface::~CPlayerInterface()
 {
 	delete pim;
 	delete showingDialog;
 	delete mainFPSmng;
+	if(adventureInt->active & CIntObject::KEYBOARD)
+		adventureInt->deactivateKeys();
 	delete adventureInt;
 	delete cingconsole;
 
@@ -135,90 +141,58 @@ void CPlayerInterface::init(ICallback * CB)
 }
 void CPlayerInterface::yourTurn()
 {
-	try
-	{
-		LOCPLINT = this;
-		makingTurn = true;
-
-		static bool firstCall = true;
-		static int autosaveCount = 0;
-
-		if(firstCall)
-			firstCall = false;
-		else
-			LOCPLINT->cb->save("Autosave_" + boost::lexical_cast<std::string>(autosaveCount++ + 1));
-
-		autosaveCount %= 5;
-
-		for(std::map<int,SDL_Surface*>::iterator i=graphics->heroWins.begin(); i!=graphics->heroWins.end();i++) //redraw hero infoboxes
-			SDL_FreeSurface(i->second);
-		graphics->heroWins.clear();
-		std::vector <const CGHeroInstance *> hh = cb->getHeroesInfo(false);
-		for(int i=0;i<hh.size();i++)
-		{
-			SDL_Surface * pom = infoWin(hh[i]);
-			graphics->heroWins.insert(std::pair<int,SDL_Surface*>(hh[i]->subID,pom));
-		}
-
-		/* TODO: This isn't quite right. First day in game should play
-		 * NEWDAY. And we don't play NEWMONTH. */
-		int day = cb->getDate(1);
-		if (day != 1)
-			CGI->soundh->playSound(soundBase::newDay);
-		else
-			CGI->soundh->playSound(soundBase::newWeek);
-
-		adventureInt->infoBar.newDay(day);
-
-		//select first hero if available.
-		//TODO: check if hero is slept
-		if(wanderingHeroes.size())
-			adventureInt->select(wanderingHeroes[0]);
-		else
-			adventureInt->select(adventureInt->townList.items[0]);
+	LOCPLINT = this;
+	makingTurn = true;
 
-		adventureInt->showAll(screen);
+	if(firstCall)
+	{
+		autosaveCount = getLastIndex("Autosave_");
 		GH.pushInt(adventureInt);
 		adventureInt->activateKeys();
-
-		while(makingTurn) // main loop
+		if(firstCall > 0) //new game, not laoded
 		{
-			if (terminate)
-				break;
-
-			pim->lock();
-
-			//if there are any waiting dialogs, show them
-			if(dialogs.size() && !showingDialog->get())
-			{
-				showingDialog->set(true);
-				GH.pushInt(dialogs.front());
-				dialogs.pop_front();
-			}
+			int index = getLastIndex("Newgame_Autosave_");
+			index %= SAVES_COUNT;
+			cb->save("Newgame_Autosave_" + boost::lexical_cast<std::string>(index + 1));
+		}
+		firstCall = 0;
+	}
+	else
+	{
+		LOCPLINT->cb->save("Autosave_" + boost::lexical_cast<std::string>(autosaveCount++ + 1));
+		autosaveCount %= 5;
+	}
 
-			GH.updateTime();
-			GH.handleEvents();
+	for(std::map<int,SDL_Surface*>::iterator i=graphics->heroWins.begin(); i!=graphics->heroWins.end();i++) //redraw hero infoboxes
+		SDL_FreeSurface(i->second);
+	graphics->heroWins.clear();
+	std::vector <const CGHeroInstance *> hh = cb->getHeroesInfo(false);
+	for(int i=0;i<hh.size();i++)
+	{
+		SDL_Surface * pom = infoWin(hh[i]);
+		graphics->heroWins.insert(std::pair<int,SDL_Surface*>(hh[i]->subID,pom));
+	}
 
-			if(!adventureInt->active && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled
-				GH.totalRedraw();
-			else
-				GH.simpleRedraw();
+	/* TODO: This isn't quite right. First day in game should play
+	 * NEWDAY. And we don't play NEWMONTH. */
+	int day = cb->getDate(1);
+	if (day != 1)
+		CGI->soundh->playSound(soundBase::newDay);
+	else
+		CGI->soundh->playSound(soundBase::newWeek);
 
-			CGI->curh->draw1();
-			CSDL_Ext::update(screen);
-			CGI->curh->draw2();
-			pim->unlock();
-			SDL_framerateDelay(mainFPSmng);
-		}
+	adventureInt->infoBar.newDay(day);
 
-		adventureInt->deactivateKeys();
-		GH.popInt(adventureInt);
+	//select first hero if available.
+	//TODO: check if hero is slept
+	if(wanderingHeroes.size())
+		adventureInt->select(wanderingHeroes[0]);
+	else
+		adventureInt->select(adventureInt->townList.items[0]);
 
-		cb->endTurn();
-	} HANDLE_EXCEPTION;
+	adventureInt->showAll(screen);
 
-	if (terminate)
-		terminate_cond.set(true);
+	GH.curInt = this;
 }
 
 inline void subRect(const int & x, const int & y, const int & z, const SDL_Rect & r, const int & hid)
@@ -1338,6 +1312,7 @@ void CPlayerInterface::serialize( CISer<CLoadFile> &h, const int version )
 {
 	serializeTempl(h,version);
 	sysOpts.apply();
+	firstCall = -1;
 }
 
 void CPlayerInterface::redrawHeroWin(const CGHeroInstance * hero)
@@ -1526,7 +1501,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
 	LOCPLINT->adventureInt->centerOn (pos);
 	if(focusTime)
 	{
-		bool activeAdv = (GH.topInt() == adventureInt  &&  adventureInt->active);
+		bool activeAdv = (GH.topInt() == adventureInt  &&  adventureInt->isActive());
 		if(activeAdv)
 			adventureInt->deactivate();
 
@@ -1551,6 +1526,64 @@ bool CPlayerInterface::ctrlPressed() const
 	return SDL_GetKeyState(NULL)[SDLK_LCTRL]  ||  SDL_GetKeyState(NULL)[SDLK_RCTRL];
 }
 
+void CPlayerInterface::update()
+{
+	pim->lock();
+
+	//if there are any waiting dialogs, show them
+	if(dialogs.size() && !showingDialog->get())
+	{
+		showingDialog->set(true);
+		GH.pushInt(dialogs.front());
+		dialogs.pop_front();
+	}
+
+	GH.updateTime();
+	GH.handleEvents();
+
+	if(!adventureInt->isActive() && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled
+		GH.totalRedraw();
+	else
+		GH.simpleRedraw();
+
+	CGI->curh->draw1();
+	CSDL_Ext::update(screen);
+	CGI->curh->draw2();
+
+	pim->unlock();
+
+	SDL_framerateDelay(mainFPSmng);
+}
+
+int CPlayerInterface::getLastIndex( std::string namePrefix)
+{
+	using namespace boost::filesystem;
+	using namespace boost::algorithm;
+
+	std::map<std::time_t, int> dates; //save number => datestamp
+
+	directory_iterator enddir;
+	for (directory_iterator dir(DATA_DIR "/Games"); dir!=enddir; dir++)
+	{
+		if(is_regular(dir->status()))
+		{
+			std::string name = dir->path().leaf();
+			if(starts_with(name, namePrefix) && ends_with(name, ".vlgm1"))
+			{
+				char nr = name[namePrefix.size()];
+				if(std::isdigit(nr))
+				{
+					dates[last_write_time(dir->path())] = boost::lexical_cast<int>(nr);
+				}
+			}
+		}
+	}
+
+	if(dates.size())
+		return (--dates.end())->second; //return latest file number
+	return 0;
+}
+
 void SystemOptions::setMusicVolume( int newVolume )
 {
 	musicVolume = newVolume;

+ 8 - 1
client/CPlayerInterface.h

@@ -7,6 +7,7 @@
 #include <map>
 #include <list>
 #include <algorithm>
+#include "GUIBase.h"
 
 #ifdef __GNUC__
 #define sprintf_s snprintf 
@@ -106,7 +107,7 @@ struct SystemOptions
 
 extern SystemOptions GDefaultOptions; //defined and inited in CMT.cpp, stores default settings loaded with application
 
-class CPlayerInterface : public CGameInterface
+class CPlayerInterface : public CGameInterface, public IUpdateable
 {
 public:
 	//minor interfaces
@@ -114,6 +115,9 @@ public:
 
 	boost::recursive_mutex *pim;
 	bool makingTurn; //if player is already making his turn
+	int firstCall; // -1 - just loaded game; 1 - just started game; 0 otherwise
+	int autosaveCount;
+	static const int SAVES_COUNT = 5;
 
 	SystemOptions sysOpts;
 
@@ -131,8 +135,11 @@ public:
 
 
 	std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
+
+	void update();
 	void recreateWanderingHeroes();
 	const CGHeroInstance *getWHero(int pos); //returns NULL if position is not valid
+	int getLastIndex(std::string namePrefix);
 
 	//overloaded funcs from CGameInterface
 	void buildChanged(const CGTownInstance *town, int buildingID, int what); //what: 1 - built, 2 - demolished

+ 20 - 27
client/CPreGame.cpp

@@ -122,38 +122,11 @@ void CMenuScreen::moveTo( CMenuScreen *next )
 	GH.pushInt(next);
 }
 
-void CGPreGame::run()
-{
-	GH.handleEvents();
-
-	while(!terminate)
-	{
-		if (GH.listInt.size() == 0)
-		{
-		#ifdef _WIN32
-			CGI->videoh->open("ACREDIT.SMK");
-		#else
-			CGI->videoh->open("ACREDIT.SMK", true, false);
-		#endif
-			GH.pushInt(scrs[mainMenu]);
-		}
-
-		CGI->curh->draw1();
-		SDL_Flip(screen);
-		CGI->curh->draw2();
-		SDL_Delay(20); //give time for other apps
-		GH.topInt()->show(screen);
-		GH.updateTime();
-		GH.handleEvents();
-	}
-}
-
 CGPreGame::CGPreGame()
 {
 	GH.defActionsDef = 63;
 	CGP = this;
 	mainbg = BitmapHandler::loadBitmap("ZPIC1005.bmp");
-	terminate = false;
 
 	for(int i = 0; i < ARRAY_COUNT(scrs); i++)
 		scrs[i] = new CMenuScreen((EState)i);
@@ -193,6 +166,26 @@ void CGPreGame::disposeGraphics()
 	SDL_FreeSurface(nTown);
 }
 
+void CGPreGame::update()
+{
+	if (GH.listInt.size() == 0)
+	{
+	#ifdef _WIN32
+		CGI->videoh->open("ACREDIT.SMK");
+	#else
+		CGI->videoh->open("ACREDIT.SMK", true, false);
+	#endif
+		GH.pushInt(scrs[mainMenu]);
+	}
+
+	CGI->curh->draw1();
+	SDL_Flip(screen);
+	CGI->curh->draw2();
+	GH.topInt()->show(screen);
+	GH.updateTime();
+	GH.handleEvents();
+}
+
 CSelectionScreen::CSelectionScreen( EState Type )
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;

+ 2 - 3
client/CPreGame.h

@@ -212,7 +212,7 @@ public:
 	~CScenarioInfo();
 };
 
-class CGPreGame : public CIntObject
+class CGPreGame : public CIntObject, public IUpdateable
 {
 public:
 	SDL_Surface *mainbg;
@@ -222,10 +222,9 @@ public:
 	CDefHandler *bonuses;
 	CDefHandler *victory, *loss;
 
-	bool terminate;
-
 	CGPreGame();
 	~CGPreGame();
+	void update();
 	void run();
 	void openSel(EState type);
 	void loadGraphics();

+ 17 - 7
client/Client.cpp

@@ -83,6 +83,7 @@ public:
 
 void CClient::init()
 {
+	connectionHandler = NULL;
 	pathInfo = NULL;
 	applier = new CCLApplier;
 	IObjectInterface::cb = this;
@@ -124,17 +125,14 @@ void CClient::waitForMoveAndSend(int color)
 	}HANDLE_EXCEPTION
 	tlog1 << "We should not be here!" << std::endl;
 }
+
 void CClient::run()
 {
 	try
 	{
 		CPack *pack;
-		while(1)
+		while(!terminate)
 		{
-			if (terminate) {
-				break;
-			}
-
 			//get the package from the server
 			{
 				boost::unique_lock<boost::mutex> lock(*serv->rmx);
@@ -143,7 +141,8 @@ void CClient::run()
 				tlog5 << "\treceived server message of type " << typeid(*pack).name() << std::endl;
 			}
 
-			if (terminate) {
+			if (terminate) 
+			{
 				delete pack;
 				break;
 			}
@@ -174,6 +173,7 @@ void CClient::stop()
 	// Tell the network thread and interface thread to reach a stable state
 	terminate = true;
 	LOCPLINT->terminate = true;
+	endGame();
 }
 
 void CClient::save(const std::string & fname)
@@ -190,6 +190,10 @@ void CClient::save(const std::string & fname)
 void CClient::endGame()
 {
 	tlog0 << "\n\nEnding current game!" << std::endl;
+	GH.curInt = NULL;
+	GH.topInt()->deactivate();
+	GH.listInt.clear();
+	GH.objsToBlit.clear();
 
 	delete CGI->mh;
 	CGI->mh = NULL;
@@ -197,6 +201,7 @@ void CClient::endGame()
 	delete CGI->state;
 	CGI->state = NULL;
 
+	LOCPLINT = NULL;
 	while (!playerint.empty())
 	{
 		delete playerint.begin()->second;
@@ -208,7 +213,8 @@ void CClient::endGame()
 		delete cb;
 	}
 
-	if (serv) {
+	if (serv) 
+	{
 		tlog3 << "Connection has been requested to be closed.\n";
 		boost::unique_lock<boost::mutex>(*serv->wmx);
 		*serv << &CloseServer();
@@ -219,6 +225,10 @@ void CClient::endGame()
 		serv = NULL;
 		tlog3 << "Our socket has been closed." << std::endl;
 	}
+
+	connectionHandler->join();
+	delete connectionHandler;
+	connectionHandler = NULL;
 }
 
 void CClient::loadGame( const std::string & fname )

+ 2 - 1
client/Client.h

@@ -81,7 +81,8 @@ public:
 	void run();
 	void stop();
 
-	bool terminate;				 // tell to terminate
+	bool terminate;	// tell to terminate
+	boost::thread *connectionHandler; //thread running run() method
 
 	//////////////////////////////////////////////////////////////////////////
 	//from IGameCallback

+ 28 - 0
client/GUIBase.cpp

@@ -331,6 +331,32 @@ void CGuiHandler::fakeMouseMove()
 	handleMoveInterested(sme);
 }
 
+void CGuiHandler::run()
+{
+	try
+	{
+		while(!terminate)
+		{
+			if(curInt)
+				curInt->update();
+			SDL_Delay(20); //give time for other apps
+		}
+	} HANDLE_EXCEPTION
+}
+
+CGuiHandler::CGuiHandler()
+:lastClick(-500, -500)
+{
+	curInt = NULL;
+	current = NULL;
+	terminate = false;
+}
+
+CGuiHandler::~CGuiHandler()
+{
+
+}
+
 void CIntObject::activateLClick()
 {
 	GH.lclickable.push_front(this);
@@ -462,6 +488,8 @@ CIntObject::CIntObject()
 			pos.y = parent->pos.y;
 		}
 	}
+	else
+		parent = NULL;
 }
 
 void CIntObject::show( SDL_Surface * to )

+ 12 - 0
client/GUIBase.h

@@ -287,6 +287,13 @@ public:
 	virtual ~IShowActivable(){}; //d-tor
 };
 
+class IUpdateable
+{
+public:
+	virtual void update()=0;
+	virtual ~IUpdateable(){}; //d-tor
+};
+
 class CIntObject : public IShowActivable //interface object
 {
 public:
@@ -440,10 +447,15 @@ public:
 	std::vector<IShowable*> objsToBlit;
 
 	SDL_Event * current; //current event
+	IUpdateable *curInt;
 
 	Point lastClick;
 	unsigned lastClickTime;
+	bool terminate;
 
+	CGuiHandler();
+	~CGuiHandler();
+	void run();
 	void totalRedraw(); //forces total redraw (using showAll)
 	void simpleRedraw(); //update only top interface and draw background from buffer
 	void popInt(IShowActivable *top); //removes given interface from the top and activates next

+ 20 - 3
client/NetPacksClient.cpp

@@ -150,7 +150,18 @@ void RemoveObject::applyCl( CClient *cl )
 void TryMoveHero::applyFirstCl( CClient *cl )
 {
 	CGHeroInstance *h = GS(cl)->getHero(id);
-	if(result == TELEPORTATION  ||  result == EMBARK  ||  result == DISEMBARK)
+
+	//check if playerint will have the knowledge about movement - if not, directly update maphandler
+	for(std::map<ui8, CGameInterface*>::iterator i=cl->playerint.begin();i!=cl->playerint.end();i++)
+	{
+		if(i->first >= PLAYER_LIMIT)
+			continue;
+		PlayerState &p = GS(cl)->players[i->first];
+		if((p.fogOfWarMap[start.x-1][start.y][start.z] || p.fogOfWarMap[end.x-1][end.y][end.z]) && p.human)
+			humanKnows = true;
+	}
+
+	if(result == TELEPORTATION  ||  result == EMBARK  ||  result == DISEMBARK  ||  !humanKnows)
 		CGI->mh->removeObject(h);
 
 
@@ -179,11 +190,17 @@ void TryMoveHero::applyCl( CClient *cl )
 	for(std::map<ui8, CGameInterface*>::iterator i=cl->playerint.begin();i!=cl->playerint.end();i++)
 	{
 		if(i->first >= PLAYER_LIMIT) continue;
-		if(GS(cl)->players[i->first].fogOfWarMap[start.x-1][start.y][start.z] || GS(cl)->players[i->first].fogOfWarMap[end.x-1][end.y][end.z])
+		PlayerState &p = GS(cl)->players[i->first];
+		if(p.fogOfWarMap[start.x-1][start.y][start.z] || p.fogOfWarMap[end.x-1][end.y][end.z])
 		{
 			i->second->heroMoved(*this);
 		}
 	}
+
+	if(!humanKnows) //maphandler didn't get update from playerint, do it now
+	{				//TODO: restructure nicely
+		CGI->mh->printObject(h);
+	}
 }
 
 void SetGarrisons::applyCl( CClient *cl )
@@ -538,7 +555,7 @@ void PlayerBlocked::applyCl( CClient *cl )
 
 void YourTurn::applyCl( CClient *cl )
 {
-	boost::thread(boost::bind(&CGameInterface::yourTurn,cl->playerint[player]));
+	INTERFACE_CALL_IF_PRESENT(player,yourTurn);
 }
 
 void SaveGame::applyCl(CClient *cl)

+ 2 - 1
hch/CLodHandler.cpp

@@ -187,7 +187,8 @@ void CLodHandler::init(std::string lodFile, std::string dirName)
 
 	LOD.open(lodFile.c_str(), std::ios::in | std::ios::binary);
 
-	if (!LOD.is_open()) {
+	if (!LOD.is_open()) 
+	{
 		tlog1 << "Cannot open " << lodFile << std::endl;
 		return;
 	}

+ 3 - 1
lib/NetPacks.h

@@ -405,7 +405,7 @@ struct RemoveObject : public CPackForClient //500
 }; 
 struct TryMoveHero : public CPackForClient //501
 {
-	TryMoveHero(){type = 501;};
+	TryMoveHero(){type = 501;humanKnows=false;};
 	void applyFirstCl(CClient *cl);
 	void applyCl(CClient *cl);
 	void applyGs(CGameState *gs);
@@ -420,6 +420,8 @@ struct TryMoveHero : public CPackForClient //501
 	int3 start, end;
 	std::set<int3> fowRevealed; //revealed tiles
 
+	bool humanKnows; //used locally during applying to client
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & id & result & start & end & movePoints & fowRevealed;