Browse Source

Merge pull request #2005 from IvanSavenko/advmap_refactor

Refactoring of adventure map interface
Ivan Savenko 2 years ago
parent
commit
48abf46824
44 changed files with 1097 additions and 1027 deletions
  1. 2 2
      AI/Nullkiller/AIGateway.cpp
  2. 2 2
      AI/Nullkiller/AIGateway.h
  3. 2 2
      AI/VCAI/VCAI.cpp
  4. 2 2
      AI/VCAI/VCAI.h
  5. 4 2
      client/CMakeLists.txt
  6. 203 379
      client/CPlayerInterface.cpp
  7. 45 98
      client/CPlayerInterface.h
  8. 1 1
      client/Client.cpp
  9. 1 1
      client/Client.h
  10. 6 7
      client/ClientCommandManager.cpp
  11. 268 0
      client/PlayerLocalState.cpp
  12. 111 0
      client/PlayerLocalState.h
  13. 270 354
      client/adventureMap/CAdventureMapInterface.cpp
  14. 68 64
      client/adventureMap/CAdventureMapInterface.h
  15. 2 3
      client/adventureMap/CAdventureOptions.cpp
  16. 2 2
      client/adventureMap/CInGameConsole.cpp
  17. 7 6
      client/adventureMap/CInfoBar.cpp
  18. 21 20
      client/adventureMap/CList.cpp
  19. 10 4
      client/adventureMap/CMinimap.cpp
  20. 2 3
      client/adventureMap/CMinimap.h
  21. 1 1
      client/adventureMap/MapAudioPlayer.cpp
  22. 1 1
      client/battle/BattleInterface.cpp
  23. 0 8
      client/battle/BattleStacksController.cpp
  24. 6 6
      client/mapView/MapRendererContext.cpp
  25. 1 1
      client/mapView/MapRendererContextState.cpp
  26. 1 1
      client/mapView/MapView.cpp
  27. 1 1
      client/mapView/MapViewActions.cpp
  28. 1 1
      client/mapView/MapViewController.cpp
  29. 8 7
      client/windows/CCastleInterface.cpp
  30. 1 1
      client/windows/CQuestLog.cpp
  31. 8 7
      client/windows/CSpellWindow.cpp
  32. 7 6
      client/windows/InfoWindows.cpp
  33. 1 1
      lib/CGameInfoCallback.cpp
  34. 2 3
      lib/CGameInfoCallback.h
  35. 1 1
      lib/CGameState.cpp
  36. 1 1
      lib/CPathfinder.h
  37. 2 2
      lib/IGameCallback.cpp
  38. 3 4
      lib/IGameCallback.h
  39. 2 2
      lib/IGameEventsReceiver.h
  40. 2 2
      lib/NetPacks.h
  41. 2 2
      lib/NetPacksLib.cpp
  42. 12 12
      lib/int3.h
  43. 3 3
      server/CGameHandler.cpp
  44. 1 1
      server/CGameHandler.h

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -256,7 +256,7 @@ void AIGateway::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;
 }
 }
 
 
-void AIGateway::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
+void AIGateway::tileHidden(const std::unordered_set<int3> & pos)
 {
 {
 	LOG_TRACE(logAi);
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;
@@ -264,7 +264,7 @@ void AIGateway::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
 	nullkiller->memory->removeInvisibleObjects(myCb.get());
 	nullkiller->memory->removeInvisibleObjects(myCb.get());
 }
 }
 
 
-void AIGateway::tileRevealed(const std::unordered_set<int3, ShashInt3> & pos)
+void AIGateway::tileRevealed(const std::unordered_set<int3> & pos)
 {
 {
 	LOG_TRACE(logAi);
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;

+ 2 - 2
AI/Nullkiller/AIGateway.h

@@ -127,7 +127,7 @@ public:
 	void heroMoved(const TryMoveHero & details, bool verbose = true) override;
 	void heroMoved(const TryMoveHero & details, bool verbose = true) override;
 	void heroInGarrisonChange(const CGTownInstance * town) override;
 	void heroInGarrisonChange(const CGTownInstance * town) override;
 	void centerView(int3 pos, int focusTime) override;
 	void centerView(int3 pos, int focusTime) override;
-	void tileHidden(const std::unordered_set<int3, ShashInt3> & pos) override;
+	void tileHidden(const std::unordered_set<int3> & pos) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
 	void showTavernWindow(const CGObjectInstance * townOrTavern) override;
 	void showTavernWindow(const CGObjectInstance * townOrTavern) override;
@@ -142,7 +142,7 @@ public:
 	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
 	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
 	void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
 	void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
 	void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
 	void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
-	void tileRevealed(const std::unordered_set<int3, ShashInt3> & pos) override;
+	void tileRevealed(const std::unordered_set<int3> & pos) override;
 	void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
 	void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
 	void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
 	void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
 	void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
 	void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;

+ 2 - 2
AI/VCAI/VCAI.cpp

@@ -268,7 +268,7 @@ void VCAI::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * to
 	//moveCreaturesToHero(town);
 	//moveCreaturesToHero(town);
 }
 }
 
 
-void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
+void VCAI::tileHidden(const std::unordered_set<int3> & pos)
 {
 {
 	LOG_TRACE(logAi);
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;
@@ -277,7 +277,7 @@ void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
 	clearPathsInfo();
 	clearPathsInfo();
 }
 }
 
 
-void VCAI::tileRevealed(const std::unordered_set<int3, ShashInt3> & pos)
+void VCAI::tileRevealed(const std::unordered_set<int3> & pos)
 {
 {
 	LOG_TRACE(logAi);
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;

+ 2 - 2
AI/VCAI/VCAI.h

@@ -160,7 +160,7 @@ public:
 	void heroMoved(const TryMoveHero & details, bool verbose = true) override;
 	void heroMoved(const TryMoveHero & details, bool verbose = true) override;
 	void heroInGarrisonChange(const CGTownInstance * town) override;
 	void heroInGarrisonChange(const CGTownInstance * town) override;
 	void centerView(int3 pos, int focusTime) override;
 	void centerView(int3 pos, int focusTime) override;
-	void tileHidden(const std::unordered_set<int3, ShashInt3> & pos) override;
+	void tileHidden(const std::unordered_set<int3> & pos) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
 	void showTavernWindow(const CGObjectInstance * townOrTavern) override;
 	void showTavernWindow(const CGObjectInstance * townOrTavern) override;
@@ -175,7 +175,7 @@ public:
 	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
 	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
 	void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
 	void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
 	void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
 	void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
-	void tileRevealed(const std::unordered_set<int3, ShashInt3> & pos) override;
+	void tileRevealed(const std::unordered_set<int3> & pos) override;
 	void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
 	void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
 	void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
 	void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
 	void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
 	void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;

+ 4 - 2
client/CMakeLists.txt

@@ -3,7 +3,7 @@ set(client_SRCS
 	../CCallback.cpp
 	../CCallback.cpp
 
 
 	adventureMap/CAdvMapPanel.cpp
 	adventureMap/CAdvMapPanel.cpp
-	adventureMap/CAdvMapInt.cpp
+	adventureMap/CAdventureMapInterface.cpp
 	adventureMap/CAdventureOptions.cpp
 	adventureMap/CAdventureOptions.cpp
 	adventureMap/CInGameConsole.cpp
 	adventureMap/CInGameConsole.cpp
 	adventureMap/CInfoBar.cpp
 	adventureMap/CInfoBar.cpp
@@ -110,6 +110,7 @@ set(client_SRCS
 	CMT.cpp
 	CMT.cpp
 	CMusicHandler.cpp
 	CMusicHandler.cpp
 	CPlayerInterface.cpp
 	CPlayerInterface.cpp
+	PlayerLocalState.cpp
 	CServerHandler.cpp
 	CServerHandler.cpp
 	CVideoHandler.cpp
 	CVideoHandler.cpp
 	Client.cpp
 	Client.cpp
@@ -122,7 +123,7 @@ set(client_HEADERS
 	StdInc.h
 	StdInc.h
 
 
 	adventureMap/CAdvMapPanel.h
 	adventureMap/CAdvMapPanel.h
-	adventureMap/CAdvMapInt.h
+	adventureMap/CAdventureMapInterface.h
 	adventureMap/CAdventureOptions.h
 	adventureMap/CAdventureOptions.h
 	adventureMap/CInGameConsole.h
 	adventureMap/CInGameConsole.h
 	adventureMap/CInfoBar.h
 	adventureMap/CInfoBar.h
@@ -238,6 +239,7 @@ set(client_HEADERS
 	CMT.h
 	CMT.h
 	CMusicHandler.h
 	CMusicHandler.h
 	CPlayerInterface.h
 	CPlayerInterface.h
+	PlayerLocalState.h
 	CServerHandler.h
 	CServerHandler.h
 	CVideoHandler.h
 	CVideoHandler.h
 	Client.h
 	Client.h

+ 203 - 379
client/CPlayerInterface.cpp

@@ -8,14 +8,14 @@
  *
  *
  */
  */
 #include "StdInc.h"
 #include "StdInc.h"
+#include "CPlayerInterface.h"
 
 
 #include <vcmi/Artifact.h>
 #include <vcmi/Artifact.h>
 
 
-#include "adventureMap/CAdvMapInt.h"
+#include "adventureMap/CAdventureMapInterface.h"
 #include "mapView/mapHandler.h"
 #include "mapView/mapHandler.h"
 #include "adventureMap/CList.h"
 #include "adventureMap/CList.h"
 #include "adventureMap/CInfoBar.h"
 #include "adventureMap/CInfoBar.h"
-#include "adventureMap/CMinimap.h"
 #include "battle/BattleInterface.h"
 #include "battle/BattleInterface.h"
 #include "battle/BattleEffectsController.h"
 #include "battle/BattleEffectsController.h"
 #include "battle/BattleFieldController.h"
 #include "battle/BattleFieldController.h"
@@ -26,12 +26,12 @@
 #include "gui/CursorHandler.h"
 #include "gui/CursorHandler.h"
 #include "windows/CKingdomInterface.h"
 #include "windows/CKingdomInterface.h"
 #include "CGameInfo.h"
 #include "CGameInfo.h"
+#include "PlayerLocalState.h"
 #include "CMT.h"
 #include "CMT.h"
 #include "windows/CHeroWindow.h"
 #include "windows/CHeroWindow.h"
 #include "windows/CCreatureWindow.h"
 #include "windows/CCreatureWindow.h"
 #include "windows/CQuestLog.h"
 #include "windows/CQuestLog.h"
 #include "windows/CPuzzleWindow.h"
 #include "windows/CPuzzleWindow.h"
-#include "CPlayerInterface.h"
 #include "widgets/CComponent.h"
 #include "widgets/CComponent.h"
 #include "widgets/Buttons.h"
 #include "widgets/Buttons.h"
 #include "windows/CTradeWindow.h"
 #include "windows/CTradeWindow.h"
@@ -115,97 +115,8 @@ struct HeroObjectRetriever
 	}
 	}
 };
 };
 
 
-HeroPathStorage::HeroPathStorage(CPlayerInterface & owner):
-	owner(owner)
-{
-}
-
-void HeroPathStorage::setPath(const CGHeroInstance *h, const CGPath & path)
-{
-	paths[h] = path;
-}
-
-const CGPath & HeroPathStorage::getPath(const CGHeroInstance *h) const
-{
-	assert(hasPath(h));
-	return paths.at(h);
-}
-
-bool HeroPathStorage::hasPath(const CGHeroInstance *h) const
-{
-	return paths.count(h) > 0;
-}
-
-bool HeroPathStorage::setPath(const CGHeroInstance *h, const int3 & destination)
-{
-	CGPath path;
-	if (!owner.cb->getPathsInfo(h)->getPath(path, destination))
-		return false;
-
-	setPath(h, path);
-	return true;
-}
-
-void HeroPathStorage::removeLastNode(const CGHeroInstance *h)
-{
-	assert(hasPath(h));
-	if (!hasPath(h))
-		return;
-
-	auto & path = paths[h];
-	path.nodes.pop_back();
-	if (path.nodes.size() < 2)  //if it was the last one, remove entire path and path with only one tile is not a real path
-		erasePath(h);
-}
-
-void HeroPathStorage::erasePath(const CGHeroInstance *h)
-{
-	paths.erase(h);
-	adventureInt->updateMoveHero(h, false);
-
-}
-
-void HeroPathStorage::verifyPath(const CGHeroInstance *h)
-{
-	if (!hasPath(h))
-		return;
-	setPath(h, getPath(h).endPos());
-}
-
-template<typename Handler>
-void HeroPathStorage::serialize(Handler & h, int version)
-{
-	std::map<const CGHeroInstance *, int3> pathsMap; //hero -> dest
-	if (h.saving)
-	{
-		for (auto &p : paths)
-		{
-			if (p.second.nodes.size())
-				pathsMap[p.first] = p.second.endPos();
-			else
-				logGlobal->debug("%s has assigned an empty path! Ignoring it...", p.first->getNameTranslated());
-		}
-		h & pathsMap;
-	}
-	else
-	{
-		h & pathsMap;
-
-		if (owner.cb)
-		{
-			for (auto &p : pathsMap)
-			{
-				CGPath path;
-				owner.cb->getPathsInfo(p.first)->getPath(path, p.second);
-				paths[p.first] = path;
-				logGlobal->trace("Restored path for hero %s leading to %s with %d nodes", p.first->nodeName(), p.second.toString(), path.nodes.size());
-			}
-		}
-	}
-}
-
 CPlayerInterface::CPlayerInterface(PlayerColor Player):
 CPlayerInterface::CPlayerInterface(PlayerColor Player):
-	paths(*this)
+	localState(std::make_unique<PlayerLocalState>(*this))
 {
 {
 	logGlobal->trace("\tHuman player interface for player %s being constructed", Player.getStr());
 	logGlobal->trace("\tHuman player interface for player %s being constructed", Player.getStr());
 	destinationTeleport = ObjectInstanceID();
 	destinationTeleport = ObjectInstanceID();
@@ -248,48 +159,68 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
 	initializeHeroTownList();
 	initializeHeroTownList();
 
 
 	// always recreate advmap interface to avoid possible memory-corruption bugs
 	// always recreate advmap interface to avoid possible memory-corruption bugs
-	adventureInt.reset(new CAdvMapInt());
+	adventureInt.reset(new CAdventureMapInterface());
 }
 }
-void CPlayerInterface::yourTurn()
+
+void CPlayerInterface::playerStartsTurn(PlayerColor player)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
+	if (!vstd::contains (GH.listInt, adventureInt))
 	{
 	{
-		boost::unique_lock<boost::mutex> lock(eventsM); //block handling events until interface is ready
-
-		LOCPLINT = this;
-		GH.curInt = this;
-		adventureInt->selection = nullptr;
+		// after map load - remove all active windows and replace them with adventure map
+		GH.popInts ((int)GH.listInt.size());
+		GH.pushInt (adventureInt);
+	}
 
 
-		NotificationHandler::notify("Your turn");
+	// remove all dialogs that do not expect query answer
+	while (GH.listInt.front() != adventureInt && !dynamic_cast<CInfoWindow*>(GH.listInt.front().get()))
+		GH.popInts(1);
 
 
-		std::string prefix = settings["session"]["saveprefix"].String();
-		int frequency = static_cast<int>(settings["general"]["saveFrequency"].Integer());
-		if (firstCall)
-		{
-			if(CSH->howManyPlayerInterfaces() == 1)
-				adventureInt->setPlayer(playerID);
+	if (player != playerID && LOCPLINT == this)
+	{
+		waitWhileDialog();
+		adventureInt->onEnemyTurnStarted(player);
+	}
+}
 
 
-			autosaveCount = getLastIndex(prefix + "Autosave_");
+void CPlayerInterface::performAutosave()
+{
+	std::string prefix = settings["session"]["saveprefix"].String();
+	int frequency = static_cast<int>(settings["general"]["saveFrequency"].Integer());
+	if(firstCall)
+	{
+		autosaveCount = getLastIndex(prefix + "Autosave_");
 
 
-			if (firstCall > 0) //new game, not loaded
-			{
-				int index = getLastIndex(prefix + "Newgame_");
-				index %= SAVES_COUNT;
-				cb->save("Saves/" + prefix + "Newgame_Autosave_" + std::to_string(index + 1));
-			}
-			firstCall = 0;
-		}
-		else if(frequency > 0 && cb->getDate() % frequency == 0)
+		if(firstCall > 0) //new game, not loaded
 		{
 		{
-			LOCPLINT->cb->save("Saves/" + prefix + "Autosave_" + std::to_string(autosaveCount++ + 1));
-			autosaveCount %= 5;
+			int index = getLastIndex(prefix + "Newgame_");
+			index %= SAVES_COUNT;
+			cb->save("Saves/" + prefix + "Newgame_Autosave_" + std::to_string(index + 1));
 		}
 		}
+		firstCall = 0;
+	}
+	else if(frequency > 0 && cb->getDate() % frequency == 0)
+	{
+		LOCPLINT->cb->save("Saves/" + prefix + "Autosave_" + std::to_string(autosaveCount++ + 1));
+		autosaveCount %= 5;
+	}
+}
+
+void CPlayerInterface::yourTurn()
+{
+	EVENT_HANDLER_CALLED_BY_CLIENT;
+	{
+		boost::unique_lock<boost::mutex> lock(eventsM); //block handling events until interface is ready
+
+		LOCPLINT = this;
+		GH.curInt = this;
 
 
-		adventureInt->setPlayer(playerID);
+		NotificationHandler::notify("Your turn");
+		performAutosave();
 
 
 		if (CSH->howManyPlayerInterfaces() > 1) //hot seat message
 		if (CSH->howManyPlayerInterfaces() > 1) //hot seat message
 		{
 		{
-			adventureInt->startHotSeatWait(playerID);
+			adventureInt->onHotseatWaitStarted(playerID);
 
 
 			makingTurn = true;
 			makingTurn = true;
 			std::string msg = CGI->generaltexth->allTexts[13];
 			std::string msg = CGI->generaltexth->allTexts[13];
@@ -301,13 +232,60 @@ void CPlayerInterface::yourTurn()
 		else
 		else
 		{
 		{
 			makingTurn = true;
 			makingTurn = true;
-			adventureInt->startTurn();
+			adventureInt->onPlayerTurnStarted(playerID);
 		}
 		}
 	}
 	}
-
 	acceptTurn();
 	acceptTurn();
 }
 }
 
 
+void CPlayerInterface::acceptTurn()
+{
+	if (settings["session"]["autoSkip"].Bool())
+	{
+		while(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
+			iw->close();
+	}
+
+	if(CSH->howManyPlayerInterfaces() > 1)
+	{
+		waitWhileDialog(); // wait for player to accept turn in hot-seat mode
+
+		adventureInt->onPlayerTurnStarted(playerID);
+	}
+
+	// warn player if he has no town
+	if (cb->howManyTowns() == 0)
+	{
+		auto playerColor = *cb->getPlayerID();
+
+		std::vector<Component> components;
+		components.emplace_back(Component::EComponentType::FLAG, playerColor.getNum(), 0, 0);
+		MetaString text;
+
+		const auto & optDaysWithoutCastle = cb->getPlayerState(playerColor)->daysWithoutCastle;
+
+		if(optDaysWithoutCastle)
+		{
+			auto daysWithoutCastle = optDaysWithoutCastle.value();
+			if (daysWithoutCastle < 6)
+			{
+				text.addTxt(MetaString::ARRAY_TXT,128); //%s, you only have %d days left to capture a town or you will be banished from this land.
+				text.addReplacement(MetaString::COLOR, playerColor.getNum());
+				text.addReplacement(7 - daysWithoutCastle);
+			}
+			else if (daysWithoutCastle == 6)
+			{
+				text.addTxt(MetaString::ARRAY_TXT,129); //%s, this is your last day to capture a town or you will be banished from this land.
+				text.addReplacement(MetaString::COLOR, playerColor.getNum());
+			}
+
+			showInfoDialogAndWait(components, text);
+		}
+		else
+			logGlobal->warn("Player has no towns, but daysWithoutCastle is not set");
+	}
+}
+
 void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -324,37 +302,40 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 	if (!hero)
 	if (!hero)
 		return;
 		return;
 
 
-	adventureInt->infoBar->requestPopAll();
 	if (details.result == TryMoveHero::EMBARK || details.result == TryMoveHero::DISEMBARK)
 	if (details.result == TryMoveHero::EMBARK || details.result == TryMoveHero::DISEMBARK)
 	{
 	{
 		if(hero->getRemovalSound() && hero->tempOwner == playerID)
 		if(hero->getRemovalSound() && hero->tempOwner == playerID)
 			CCS->soundh->playSound(hero->getRemovalSound().value());
 			CCS->soundh->playSound(hero->getRemovalSound().value());
 	}
 	}
 
 
-	adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.start));
-	adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.end));
+	std::unordered_set<int3> changedTiles {
+		hero->convertToVisitablePos(details.start),
+		hero->convertToVisitablePos(details.end)
+	};
+	adventureInt->onMapTilesChanged(changedTiles);
+	adventureInt->onHeroMovementStarted(hero);
 
 
-	bool directlyAttackingCreature = details.attackedFrom && paths.hasPath(hero) && paths.getPath(hero).endPos() == *details.attackedFrom;
+	bool directlyAttackingCreature = details.attackedFrom && localState->hasPath(hero) && localState->getPath(hero).endPos() == *details.attackedFrom;
 
 
 	if(makingTurn && hero->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
 	if(makingTurn && hero->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
 	{
 	{
 		if(details.result == TryMoveHero::TELEPORTATION)
 		if(details.result == TryMoveHero::TELEPORTATION)
 		{
 		{
-			if(paths.hasPath(hero))
+			if(localState->hasPath(hero))
 			{
 			{
-				assert(paths.getPath(hero).nodes.size() >= 2);
-				auto nodesIt = paths.getPath(hero).nodes.end() - 1;
+				assert(localState->getPath(hero).nodes.size() >= 2);
+				auto nodesIt = localState->getPath(hero).nodes.end() - 1;
 
 
 				if((nodesIt)->coord == hero->convertToVisitablePos(details.start)
 				if((nodesIt)->coord == hero->convertToVisitablePos(details.start)
 					&& (nodesIt - 1)->coord == hero->convertToVisitablePos(details.end))
 					&& (nodesIt - 1)->coord == hero->convertToVisitablePos(details.end))
 				{
 				{
 					//path was between entrance and exit of teleport -> OK, erase node as usual
 					//path was between entrance and exit of teleport -> OK, erase node as usual
-					paths.removeLastNode(hero);
+					localState->removeLastNode(hero);
 				}
 				}
 				else
 				else
 				{
 				{
 					//teleport was not along current path, it'll now be invalid (hero is somewhere else)
 					//teleport was not along current path, it'll now be invalid (hero is somewhere else)
-					paths.erasePath(hero);
+					localState->erasePath(hero);
 
 
 				}
 				}
 			}
 			}
@@ -363,28 +344,26 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 		if(hero->pos != details.end //hero didn't change tile but visit succeeded
 		if(hero->pos != details.end //hero didn't change tile but visit succeeded
 			|| directlyAttackingCreature) // or creature was attacked from endangering tile.
 			|| directlyAttackingCreature) // or creature was attacked from endangering tile.
 		{
 		{
-			paths.erasePath(hero);
+			localState->erasePath(hero);
 		}
 		}
-		else if(paths.hasPath(hero) && hero->pos == details.end) //&& hero is moving
+		else if(localState->hasPath(hero) && hero->pos == details.end) //&& hero is moving
 		{
 		{
 			if(details.start != details.end) //so we don't touch path when revisiting with spacebar
 			if(details.start != details.end) //so we don't touch path when revisiting with spacebar
-				paths.removeLastNode(hero);
+				localState->removeLastNode(hero);
 		}
 		}
 	}
 	}
 
 
 	if(details.stopMovement()) //hero failed to move
 	if(details.stopMovement()) //hero failed to move
 	{
 	{
 		stillMoveHero.setn(STOP_MOVE);
 		stillMoveHero.setn(STOP_MOVE);
-		adventureInt->heroList->update(hero);
+		adventureInt->onHeroChanged(hero);
 		return;
 		return;
 	}
 	}
 
 
-	adventureInt->heroList->redraw();
-
 	CGI->mh->waitForOngoingAnimations();
 	CGI->mh->waitForOngoingAnimations();
 
 
 	//move finished
 	//move finished
-	adventureInt->heroList->update(hero);
+	adventureInt->onHeroChanged(hero);
 
 
 	//check if user cancelled movement
 	//check if user cancelled movement
 	{
 	{
@@ -428,33 +407,9 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->getNameTranslated() % playerID);
 	LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->getNameTranslated() % playerID);
 
 
-	const CArmedInstance *newSelection = nullptr;
-	if (makingTurn)
-	{
-		//find new object for selection: either hero
-		int next = adventureInt->getNextHeroIndex(vstd::find_pos(wanderingHeroes, hero));
-		if (next >= 0)
-			newSelection = wanderingHeroes[next];
-
-		//or town
-		if (!newSelection || newSelection == hero)
-		{
-			if (towns.empty())
-				newSelection = nullptr;
-			else
-				newSelection = towns.front();
-		}
-	}
-
-	wanderingHeroes -= hero;
-
-	adventureInt->heroList->update(hero);
-	if (makingTurn && newSelection)
-		adventureInt->select(newSelection, true);
-	else if (adventureInt->selection == hero)
-		adventureInt->selection = nullptr;
-
-	paths.erasePath(hero);
+	localState->removeWanderingHero(hero);
+	adventureInt->onHeroChanged(hero);
+	localState->erasePath(hero);
 }
 }
 
 
 void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start)
 void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start)
@@ -470,8 +425,8 @@ void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectI
 void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	wanderingHeroes.push_back(hero);
-	adventureInt->heroList->update(hero);
+	localState->addWanderingHero(hero);
+	adventureInt->onHeroChanged(hero);
 }
 }
 void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 {
 {
@@ -484,13 +439,6 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 	GH.pushInt(newCastleInt);
 	GH.pushInt(newCastleInt);
 }
 }
 
 
-void CPlayerInterface::activateForSpectator()
-{
-	adventureInt->state = CAdvMapInt::INGAME;
-	adventureInt->activate();
-	adventureInt->minimap->activate();
-}
-
 void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val)
 void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -499,8 +447,8 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int
 		if (CAltarWindow *ctw = dynamic_cast<CAltarWindow *>(GH.topInt().get()))
 		if (CAltarWindow *ctw = dynamic_cast<CAltarWindow *>(GH.topInt().get()))
 			ctw->setExpToLevel();
 			ctw->setExpToLevel();
 	}
 	}
-	else if (which < GameConstants::PRIMARY_SKILLS) //no need to redraw infowin if this is experience (exp is treated as prim skill with id==4)
-		updateInfo(hero);
+	else
+		adventureInt->onHeroChanged(hero);
 }
 }
 
 
 void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
 void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
@@ -516,15 +464,15 @@ void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, in
 void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
 void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	updateInfo(hero);
+	adventureInt->onHeroChanged(hero);
 	if (makingTurn && hero->tempOwner == playerID)
 	if (makingTurn && hero->tempOwner == playerID)
-		adventureInt->heroList->update(hero);
+		adventureInt->onHeroChanged(hero);
 }
 }
 void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
 void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	if (makingTurn && hero->tempOwner == playerID)
 	if (makingTurn && hero->tempOwner == playerID)
-		adventureInt->heroList->update(hero);
+		adventureInt->onHeroChanged(hero);
 }
 }
 void CPlayerInterface::receivedResource()
 void CPlayerInterface::receivedResource()
 {
 {
@@ -560,21 +508,22 @@ void CPlayerInterface::commanderGotLevel (const CCommanderInstance * commander,
 void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	updateInfo(town);
 
 
-	if (town->garrisonHero) //wandering hero moved to the garrison
+	if(town->garrisonHero) //wandering hero moved to the garrison
 	{
 	{
-		if (town->garrisonHero->tempOwner == playerID && vstd::contains(wanderingHeroes,town->garrisonHero)) // our hero
-			wanderingHeroes -= town->garrisonHero;
+		// This method also gets called on hero recruitment -> garrisoned hero is already in garrison
+		if(town->garrisonHero->tempOwner == playerID && !vstd::contains(localState->getWanderingHeroes(), town->visitingHero))
+			localState->removeWanderingHero(town->garrisonHero);
 	}
 	}
 
 
-	if (town->visitingHero) //hero leaves garrison
+	if(town->visitingHero) //hero leaves garrison
 	{
 	{
-		if (town->visitingHero->tempOwner == playerID && !vstd::contains(wanderingHeroes,town->visitingHero)) // our hero
-			wanderingHeroes.push_back(town->visitingHero);
+		// This method also gets called on hero recruitment -> wandering heroes already contains new hero
+		if(town->visitingHero->tempOwner == playerID && !vstd::contains(localState->getWanderingHeroes(), town->visitingHero))
+			localState->addWanderingHero(town->visitingHero);
 	}
 	}
-	adventureInt->heroList->update();
-	adventureInt->updateNextHero(nullptr);
+	adventureInt->onHeroChanged(nullptr);
+	adventureInt->onTownChanged(town);
 
 
 	if(castleInt)
 	if(castleInt)
 	{
 	{
@@ -627,7 +576,15 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CGObjectInstance *> ob
 {
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	for (auto object : objs)
 	for (auto object : objs)
-		updateInfo(object);
+	{
+		auto * hero = dynamic_cast<const CGHeroInstance*>(object);
+		auto * town = dynamic_cast<const CGTownInstance*>(object);
+
+		if (hero)
+			adventureInt->onHeroChanged(hero);
+		if (town)
+			adventureInt->onTownChanged(town);
+	}
 
 
 	for (auto & elem : GH.listInt)
 	for (auto & elem : GH.listInt)
 	{
 	{
@@ -645,23 +602,9 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CGObjectInstance *> ob
 	GH.totalRedraw();
 	GH.totalRedraw();
 }
 }
 
 
-void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj)
-{
-	garrisonsChanged(std::vector<const CGObjectInstance *>(1, obj));
-}
-
 void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) //what: 1 - built, 2 - demolished
 void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) //what: 1 - built, 2 - demolished
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	switch (buildingID)
-	{
-	case BuildingID::FORT: case BuildingID::CITADEL: case BuildingID::CASTLE:
-	case BuildingID::VILLAGE_HALL: case BuildingID::TOWN_HALL: case BuildingID::CITY_HALL: case BuildingID::CAPITOL:
-	case BuildingID::RESOURCE_SILO:
-		updateInfo(town);
-		break;
-	}
-
 	if (castleInt)
 	if (castleInt)
 	{
 	{
 		castleInt->townlist->update(town);
 		castleInt->townlist->update(town);
@@ -680,7 +623,7 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID build
 			}
 			}
 		}
 		}
 	}
 	}
-	adventureInt->townList->update(town);
+	adventureInt->onTownChanged(town);
 }
 }
 
 
 void CPlayerInterface::battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
 void CPlayerInterface::battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
@@ -709,7 +652,6 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
 		isAutoFightOn = true;
 		isAutoFightOn = true;
 		cb->registerBattleInterface(autofightingAI);
 		cb->registerBattleInterface(autofightingAI);
 		// Player shouldn't be able to move on adventure map if quick combat is going
 		// Player shouldn't be able to move on adventure map if quick combat is going
-		adventureInt->quickCombatLock();
 		allowBattleReplay = true;
 		allowBattleReplay = true;
 	}
 	}
 
 
@@ -920,7 +862,6 @@ void CPlayerInterface::battleEnd(const BattleResult *br, QueryID queryID)
 			// #1490 - during AI turn when quick combat is on, we need to display the message and wait for user to close it.
 			// #1490 - during AI turn when quick combat is on, we need to display the message and wait for user to close it.
 			// Otherwise NewTurn causes freeze.
 			// Otherwise NewTurn causes freeze.
 			waitWhileDialog();
 			waitWhileDialog();
-			adventureInt->quickCombatUnlock();
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -928,7 +869,6 @@ void CPlayerInterface::battleEnd(const BattleResult *br, QueryID queryID)
 	BATTLE_EVENT_POSSIBLE_RETURN;
 	BATTLE_EVENT_POSSIBLE_RETURN;
 
 
 	battleInt->battleFinished(*br, queryID);
 	battleInt->battleFinished(*br, queryID);
-	adventureInt->quickCombatUnlock();
 }
 }
 
 
 void CPlayerInterface::battleLogMessage(const std::vector<MetaString> & lines)
 void CPlayerInterface::battleLogMessage(const std::vector<MetaString> & lines)
@@ -1070,7 +1010,7 @@ void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &t
 	if(autoTryHover || type == EInfoWindowMode::INFO)
 	if(autoTryHover || type == EInfoWindowMode::INFO)
 	{
 	{
 		waitWhileDialog(); //Fix for mantis #98
 		waitWhileDialog(); //Fix for mantis #98
-		adventureInt->infoBar->pushComponents(components, text, timer);
+		adventureInt->showInfoBoxMessage(components, text, timer);
 
 
 		if (makingTurn && GH.listInt.size() && LOCPLINT == this)
 		if (makingTurn && GH.listInt.size() && LOCPLINT == this)
 			CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
 			CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
@@ -1230,19 +1170,17 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
 	GH.pushInt(wnd);
 	GH.pushInt(wnd);
 }
 }
 
 
-void CPlayerInterface::tileRevealed(const std::unordered_set<int3, ShashInt3> &pos)
+void CPlayerInterface::tileRevealed(const std::unordered_set<int3> &pos)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	//FIXME: wait for dialog? Magi hut/eye would benefit from this but may break other areas
 	//FIXME: wait for dialog? Magi hut/eye would benefit from this but may break other areas
-	for (auto & po : pos)
-		adventureInt->minimap->updateTile(po);
+	adventureInt->onMapTilesChanged(pos);
 }
 }
 
 
-void CPlayerInterface::tileHidden(const std::unordered_set<int3, ShashInt3> &pos)
+void CPlayerInterface::tileHidden(const std::unordered_set<int3> &pos)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	for (auto & po : pos)
-		adventureInt->minimap->updateTile(po);
+	adventureInt->onMapTilesChanged(pos);
 }
 }
 
 
 void CPlayerInterface::openHeroWindow(const CGHeroInstance *hero)
 void CPlayerInterface::openHeroWindow(const CGHeroInstance *hero)
@@ -1286,33 +1224,24 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
 	if (bonus.type == Bonus::NONE)
 	if (bonus.type == Bonus::NONE)
 		return;
 		return;
 
 
-	updateInfo(hero);
+	adventureInt->onHeroChanged(hero);
 	if ((bonus.type == Bonus::FLYING_MOVEMENT || bonus.type == Bonus::WATER_WALKING) && !gain)
 	if ((bonus.type == Bonus::FLYING_MOVEMENT || bonus.type == Bonus::WATER_WALKING) && !gain)
 	{
 	{
 		//recalculate paths because hero has lost bonus influencing pathfinding
 		//recalculate paths because hero has lost bonus influencing pathfinding
-		paths.erasePath(hero);
+		localState->erasePath(hero);
 	}
 	}
 }
 }
 
 
-template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, const int version )
-{
-	h & wanderingHeroes;
-	h & towns;
-	h & sleepingHeroes;
-	h & paths;
-	h & spellbookSettings;
-}
-
 void CPlayerInterface::saveGame( BinarySerializer & h, const int version )
 void CPlayerInterface::saveGame( BinarySerializer & h, const int version )
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	serializeTempl(h,version);
+	localState->serialize(h, version);
 }
 }
 
 
 void CPlayerInterface::loadGame( BinaryDeserializer & h, const int version )
 void CPlayerInterface::loadGame( BinaryDeserializer & h, const int version )
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	serializeTempl(h,version);
+	localState->serialize(h, version);
 	firstCall = -1;
 	firstCall = -1;
 }
 }
 
 
@@ -1330,14 +1259,8 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, const CGPath& path )
 
 
 	setMovementStatus(true);
 	setMovementStatus(true);
 
 
-	if (adventureInt && adventureInt->isHeroSleeping(h))
-	{
-		adventureInt->sleepWake->clickLeft(true, false);
-		adventureInt->sleepWake->clickLeft(false, true);
-		//could've just called
-		//adventureInt->fsleepWake();
-		//but no authentic button click/sound ;-)
-	}
+	if (localState->isHeroSleeping(h))
+		localState->setHeroAwaken(h);
 
 
 	boost::thread moveHeroTask(std::bind(&CPlayerInterface::doMoveHero,this,h,path));
 	boost::thread moveHeroTask(std::bind(&CPlayerInterface::doMoveHero,this,h,path));
 }
 }
@@ -1347,7 +1270,7 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	auto onEnd = [=](){ cb->selectionMade(0, queryID); };
 	auto onEnd = [=](){ cb->selectionMade(0, queryID); };
 
 
-	if (stillMoveHero.get() == DURING_MOVE  && paths.hasPath(down) && paths.getPath(down).nodes.size() > 1) //to ignore calls on passing through garrisons
+	if (stillMoveHero.get() == DURING_MOVE  && localState->hasPath(down) && localState->getPath(down).nodes.size() > 1) //to ignore calls on passing through garrisons
 	{
 	{
 		onEnd();
 		onEnd();
 		return;
 		return;
@@ -1406,6 +1329,12 @@ void CPlayerInterface::requestRealized( PackageApplied *pa )
 	}
 	}
 }
 }
 
 
+
+void CPlayerInterface::showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)
+{
+	heroExchangeStarted(hero1, hero2, QueryID(-1));
+}
+
 void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query)
 void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -1415,48 +1344,51 @@ void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanc
 void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
+
 	//redraw minimap if owner changed
 	//redraw minimap if owner changed
 	if (sop->what == ObjProperty::OWNER)
 	if (sop->what == ObjProperty::OWNER)
 	{
 	{
 		const CGObjectInstance * obj = cb->getObj(sop->id);
 		const CGObjectInstance * obj = cb->getObj(sop->id);
-		std::set<int3> pos = obj->getBlockedPos();
 
 
-		for(auto & po : pos)
-		{
-			if(cb->isVisible(po))
-				adventureInt->minimap->updateTile(po);
-		}
 		if(obj->ID == Obj::TOWN)
 		if(obj->ID == Obj::TOWN)
 		{
 		{
+			auto town = static_cast<const CGTownInstance *>(obj);
+
 			if(obj->tempOwner == playerID)
 			if(obj->tempOwner == playerID)
-				towns.push_back(static_cast<const CGTownInstance *>(obj));
+				localState->addOwnedTown(town);
 			else
 			else
-				towns -= obj;
+				localState->removeOwnedTown(town);
 
 
-			adventureInt->townList->update();
-			adventureInt->minimap->update();
+			adventureInt->onTownChanged(town);
 		}
 		}
-		assert(cb->getTownsInfo().size() == towns.size());
+
+		std::set<int3> pos = obj->getBlockedPos();
+		std::unordered_set<int3> upos(pos.begin(), pos.end());
+		adventureInt->onMapTilesChanged(upos);
+
+		assert(cb->getTownsInfo().size() == localState->getOwnedTowns().size());
 	}
 	}
 }
 }
 
 
 void CPlayerInterface::initializeHeroTownList()
 void CPlayerInterface::initializeHeroTownList()
 {
 {
-	if(!wanderingHeroes.size())
+	if(localState->getWanderingHeroes().empty())
 	{
 	{
-		std::vector<const CGHeroInstance*> heroes = cb->getHeroesInfo();
-		for(auto & hero : heroes)
+		for(auto & hero : cb->getHeroesInfo())
 		{
 		{
 			if(!hero->inTownGarrison)
 			if(!hero->inTownGarrison)
-				wanderingHeroes.push_back(hero);
+				localState->addWanderingHero(hero);
 		}
 		}
 	}
 	}
 
 
-	if(!towns.size())
-		towns = cb->getTownsInfo();
+	if(localState->getOwnedTowns().empty())
+	{
+		for(auto & town : cb->getTownsInfo())
+			localState->addOwnedTown(town);
+	}
 
 
 	if(adventureInt)
 	if(adventureInt)
-		adventureInt->updateNextHero(nullptr);
+		adventureInt->onHeroChanged(nullptr);
 }
 }
 
 
 void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level)
 void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level)
@@ -1545,7 +1477,7 @@ void CPlayerInterface::objectRemoved(const CGObjectInstance * obj)
 void CPlayerInterface::objectRemovedAfter()
 void CPlayerInterface::objectRemovedAfter()
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	adventureInt->minimap->update();
+	adventureInt->onMapTilesChanged(boost::none);
 }
 }
 
 
 void CPlayerInterface::playerBlocked(int reason, bool start)
 void CPlayerInterface::playerBlocked(int reason, bool start)
@@ -1558,8 +1490,7 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
 			boost::unique_lock<boost::mutex> lock(eventsM); //TODO: copied from yourTurn, no idea if it's needed
 			boost::unique_lock<boost::mutex> lock(eventsM); //TODO: copied from yourTurn, no idea if it's needed
 			LOCPLINT = this;
 			LOCPLINT = this;
 			GH.curInt = this;
 			GH.curInt = this;
-			adventureInt->selection = nullptr;
-			adventureInt->setPlayer(playerID);
+			adventureInt->onCurrentPlayerChanged(playerID);
 			std::string msg = CGI->generaltexth->translate("vcmi.adventureMap.playerAttacked");
 			std::string msg = CGI->generaltexth->translate("vcmi.adventureMap.playerAttacked");
 			boost::replace_first(msg, "%s", cb->getStartInfo()->playerInfos.find(playerID)->second.name);
 			boost::replace_first(msg, "%s", cb->getStartInfo()->playerInfos.find(playerID)->second.name);
 			std::vector<std::shared_ptr<CComponent>> cmp;
 			std::vector<std::shared_ptr<CComponent>> cmp;
@@ -1636,10 +1567,12 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
 		if (victoryLossCheckResult.loss())
 		if (victoryLossCheckResult.loss())
 			showInfoDialog(CGI->generaltexth->allTexts[95]);
 			showInfoDialog(CGI->generaltexth->allTexts[95]);
 
 
-		//we assume GH.curInt == LOCPLINT
+		assert(GH.curInt == LOCPLINT);
 		auto previousInterface = LOCPLINT; //without multiple player interfaces some of lines below are useless, but for hotseat we wanna swap player interface temporarily
 		auto previousInterface = LOCPLINT; //without multiple player interfaces some of lines below are useless, but for hotseat we wanna swap player interface temporarily
+
 		LOCPLINT = this; //this is needed for dialog to show and avoid freeze, dialog showing logic should be reworked someday
 		LOCPLINT = this; //this is needed for dialog to show and avoid freeze, dialog showing logic should be reworked someday
 		GH.curInt = this; //waiting for dialogs requires this to get events
 		GH.curInt = this; //waiting for dialogs requires this to get events
+
 		if(!makingTurn)
 		if(!makingTurn)
 		{
 		{
 			makingTurn = true; //also needed for dialog to show with current implementation
 			makingTurn = true; //also needed for dialog to show with current implementation
@@ -1657,9 +1590,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
 			if(adventureInt)
 			if(adventureInt)
 			{
 			{
 				GH.terminate_cond->setn(true);
 				GH.terminate_cond->setn(true);
-				adventureInt->deactivate();
-				if (GH.topInt() == adventureInt)
-					GH.popInt(adventureInt);
+				GH.popInts(GH.listInt.size());
 				adventureInt.reset();
 				adventureInt.reset();
 			}
 			}
 		}
 		}
@@ -1677,7 +1608,8 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
 			requestReturningToMainMenu(false);
 			requestReturningToMainMenu(false);
 		}
 		}
 
 
-		if (GH.curInt == this) GH.curInt = nullptr;
+		if (GH.curInt == this)
+			GH.curInt = nullptr;
 	}
 	}
 	else
 	else
 	{
 	{
@@ -1720,7 +1652,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
 		GH.popInts(1);
 		GH.popInts(1);
 
 
 	if(spellID == SpellID::FLY || spellID == SpellID::WATER_WALK)
 	if(spellID == SpellID::FLY || spellID == SpellID::WATER_WALK)
-		paths.erasePath(caster);
+		localState->erasePath(caster);
 
 
 	const spells::Spell * spell = CGI->spells()->getByIndex(spellID);
 	const spells::Spell * spell = CGI->spells()->getByIndex(spellID);
 	auto castSoundPath = spell->getCastSound();
 	auto castSoundPath = spell->getCastSound();
@@ -1728,56 +1660,6 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
 		CCS->soundh->playSound(castSoundPath);
 		CCS->soundh->playSound(castSoundPath);
 }
 }
 
 
-void CPlayerInterface::acceptTurn()
-{
-	if (settings["session"]["autoSkip"].Bool())
-	{
-		while(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
-			iw->close();
-	}
-
-	if(CSH->howManyPlayerInterfaces() > 1)
-	{
-		waitWhileDialog(); // wait for player to accept turn in hot-seat mode
-
-		adventureInt->startTurn();
-	}
-
-	adventureInt->initializeNewTurn();
-
-	// warn player if he has no town
-	if (cb->howManyTowns() == 0)
-	{
-		auto playerColor = *cb->getPlayerID();
-
-		std::vector<Component> components;
-		components.emplace_back(Component::EComponentType::FLAG, playerColor.getNum(), 0, 0);
-		MetaString text;
-
-		const auto & optDaysWithoutCastle = cb->getPlayerState(playerColor)->daysWithoutCastle;
-
-		if(optDaysWithoutCastle)
-		{
-			auto daysWithoutCastle = optDaysWithoutCastle.value();
-			if (daysWithoutCastle < 6)
-			{
-				text.addTxt(MetaString::ARRAY_TXT,128); //%s, you only have %d days left to capture a town or you will be banished from this land.
-				text.addReplacement(MetaString::COLOR, playerColor.getNum());
-				text.addReplacement(7 - daysWithoutCastle);
-			}
-			else if (daysWithoutCastle == 6)
-			{
-				text.addTxt(MetaString::ARRAY_TXT,129); //%s, this is your last day to capture a town or you will be banished from this land.
-				text.addReplacement(MetaString::COLOR, playerColor.getNum());
-			}
-
-			showInfoDialogAndWait(components, text);
-		}
-		else
-			logGlobal->warn("Player has no towns, but daysWithoutCastle is not set");
-	}
-}
-
 void CPlayerInterface::tryDiggging(const CGHeroInstance * h)
 void CPlayerInterface::tryDiggging(const CGHeroInstance * h)
 {
 {
 	int msgToShow = -1;
 	int msgToShow = -1;
@@ -1810,21 +1692,6 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance * h)
 		showInfoDialog(CGI->generaltexth->allTexts[msgToShow]);
 		showInfoDialog(CGI->generaltexth->allTexts[msgToShow]);
 }
 }
 
 
-void CPlayerInterface::updateInfo(const CGObjectInstance * specific)
-{
-	bool isHero = dynamic_cast<const CGHeroInstance *>(specific) != nullptr;
-	bool changedHero = dynamic_cast<const CGHeroInstance *>(specific) != adventureInt->curHero();
-	bool isTown = dynamic_cast<const CGTownInstance *>(specific) != nullptr;
-
-	bool update = (isHero && changedHero) || (isTown);
-	// If infobar is showing components and we request an update to hero
-	// do not force infobar tick here, it will prevents us to show components just picked up
-	if(adventureInt->infoBar->showingComponents() && !update)
-		return;
-
-	adventureInt->infoBar->showSelection();
-}
-
 void CPlayerInterface::battleNewRoundFirst( int round )
 void CPlayerInterface::battleNewRoundFirst( int round )
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -1933,14 +1800,14 @@ void CPlayerInterface::artifactPut(const ArtifactLocation &al)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
-	updateInfo(hero);
+	adventureInt->onHeroChanged(hero);
 }
 }
 
 
 void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
 void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
-	updateInfo(hero);
+	adventureInt->onHeroChanged(hero);
 	for(auto isa : GH.listInt)
 	for(auto isa : GH.listInt)
 	{
 	{
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
@@ -1955,7 +1822,7 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	auto hero = std::visit(HeroObjectRetriever(), dst.artHolder);
 	auto hero = std::visit(HeroObjectRetriever(), dst.artHolder);
-	updateInfo(hero);
+	adventureInt->onHeroChanged(hero);
 
 
 	bool redraw = true;
 	bool redraw = true;
 	// If a bulk transfer has arrived, then redrawing only the last art movement.
 	// If a bulk transfer has arrived, then redrawing only the last art movement.
@@ -1984,7 +1851,7 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
-	updateInfo(hero);
+	adventureInt->onHeroChanged(hero);
 	for(auto isa : GH.listInt)
 	for(auto isa : GH.listInt)
 	{
 	{
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
@@ -1997,7 +1864,7 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
 	auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
-	updateInfo(hero);
+	adventureInt->onHeroChanged(hero);
 	for(auto isa : GH.listInt)
 	for(auto isa : GH.listInt)
 	{
 	{
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
@@ -2006,34 +1873,6 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
 	}
 	}
 }
 }
 
 
-void CPlayerInterface::playerStartsTurn(PlayerColor player)
-{
-	EVENT_HANDLER_CALLED_BY_CLIENT;
-	if (!vstd::contains (GH.listInt, adventureInt))
-	{
-		GH.popInts ((int)GH.listInt.size()); //after map load - remove everything else
-		GH.pushInt (adventureInt);
-	}
-	else
-	{
-		if (player == playerID)
-			adventureInt->infoBar->showSelection();
-		while (GH.listInt.front() != adventureInt && !dynamic_cast<CInfoWindow*>(GH.listInt.front().get())) //don't remove dialogs that expect query answer
-			GH.popInts(1);
-	}
-
-	if(CSH->howManyPlayerInterfaces() == 1)
-	{
-		GH.curInt = this;
-		adventureInt->startTurn();
-	}
-	if (player != playerID && this == LOCPLINT)
-	{
-		waitWhileDialog();
-		adventureInt->aiTurnStarted();
-	}
-}
-
 void CPlayerInterface::waitForAllDialogs(bool unlockPim)
 void CPlayerInterface::waitForAllDialogs(bool unlockPim)
 {
 {
 	while(!dialogs.empty())
 	while(!dialogs.empty())
@@ -2049,15 +1888,9 @@ void CPlayerInterface::proposeLoadingGame()
 	showYesNoDialog(CGI->generaltexth->allTexts[68], [](){ GH.pushUserEvent(EUserEvent::RETURN_TO_MENU_LOAD); }, nullptr);
 	showYesNoDialog(CGI->generaltexth->allTexts[68], [](){ GH.pushUserEvent(EUserEvent::RETURN_TO_MENU_LOAD); }, nullptr);
 }
 }
 
 
-CPlayerInterface::SpellbookLastSetting::SpellbookLastSetting()
-{
-	spellbookLastPageBattle = spellbokLastPageAdvmap = 0;
-	spellbookLastTabBattle = spellbookLastTabAdvmap = 4;
-}
-
 bool CPlayerInterface::capturedAllEvents()
 bool CPlayerInterface::capturedAllEvents()
 {
 {
-	if (duringMovement)
+	if(duringMovement)
 	{
 	{
 		//just inform that we are capturing events. they will be processed by heroMoved() in client thread.
 		//just inform that we are capturing events. they will be processed by heroMoved() in client thread.
 		return true;
 		return true;
@@ -2065,7 +1898,7 @@ bool CPlayerInterface::capturedAllEvents()
 
 
 	bool needToLockAdventureMap = adventureInt && adventureInt->active && CGI->mh->hasOngoingAnimations();
 	bool needToLockAdventureMap = adventureInt && adventureInt->active && CGI->mh->hasOngoingAnimations();
 
 
-	if (ignoreEvents || needToLockAdventureMap)
+	if (ignoreEvents || needToLockAdventureMap || isAutoFightOn)
 	{
 	{
 		boost::unique_lock<boost::mutex> un(eventsM);
 		boost::unique_lock<boost::mutex> un(eventsM);
 		while(!SDLEventsQueue.empty())
 		while(!SDLEventsQueue.empty())
@@ -2245,15 +2078,6 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 	if (!showingDialog->get())
 	if (!showingDialog->get())
 		GH.fakeMouseMove();
 		GH.fakeMouseMove();
 
 
-
-	//todo: this should be in main thread
-	if (adventureInt)
-	{
-		// (i == 0) means hero went through all the path
-		adventureInt->updateMoveHero(h, (i != 0));
-		adventureInt->updateNextHero(h);
-	}
-
 	CGI->mh->waitForOngoingAnimations();
 	CGI->mh->waitForOngoingAnimations();
 	setMovementStatus(false);
 	setMovementStatus(false);
 }
 }

+ 45 - 98
client/CPlayerInterface.h

@@ -9,16 +9,10 @@
  */
  */
 #pragma once
 #pragma once
 
 
-
 #include "../lib/FunctionList.h"
 #include "../lib/FunctionList.h"
 #include "../lib/CGameInterface.h"
 #include "../lib/CGameInterface.h"
-#include "../lib/NetPacksBase.h"
 #include "gui/CIntObject.h"
 #include "gui/CIntObject.h"
 
 
-#ifdef __GNUC__
-#define sprintf_s snprintf
-#endif
-
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
 class Artifact;
 class Artifact;
@@ -37,7 +31,7 @@ struct CPathsInfo;
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END
 
 
 class CButton;
 class CButton;
-class CAdvMapInt;
+class CAdventureMapInterface;
 class CCastleInterface;
 class CCastleInterface;
 class BattleInterface;
 class BattleInterface;
 class CComponent;
 class CComponent;
@@ -51,103 +45,63 @@ class ClickableR;
 class Hoverable;
 class Hoverable;
 class KeyInterested;
 class KeyInterested;
 class MotionInterested;
 class MotionInterested;
+class PlayerLocalState;
 class TimeInterested;
 class TimeInterested;
 class IShowable;
 class IShowable;
 
 
-struct SDL_Surface;
-union SDL_Event;
-
 namespace boost
 namespace boost
 {
 {
 	class mutex;
 	class mutex;
 	class recursive_mutex;
 	class recursive_mutex;
 }
 }
 
 
-class CPlayerInterface;
-
-class HeroPathStorage
-{
-	CPlayerInterface & owner;
-
-	std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
-
-public:
-	explicit HeroPathStorage(CPlayerInterface &owner);
-
-	void setPath(const CGHeroInstance *h, const CGPath & path);
-	bool setPath(const CGHeroInstance *h, const int3 & destination);
-
-	const CGPath & getPath(const CGHeroInstance *h) const;
-	bool hasPath(const CGHeroInstance *h) const;
-
-	void removeLastNode(const CGHeroInstance *h);
-	void erasePath(const CGHeroInstance *h);
-	void verifyPath(const CGHeroInstance *h);
-
-	template <typename Handler>
-	void serialize( Handler &h, int version );
-};
-
 /// Central class for managing user interface logic
 /// Central class for managing user interface logic
 class CPlayerInterface : public CGameInterface, public IUpdateable
 class CPlayerInterface : public CGameInterface, public IUpdateable
 {
 {
-public:
-	HeroPathStorage paths;
+	bool duringMovement;
+	bool ignoreEvents;
+	size_t numOfMovedArts;
+
+	// -1 - just loaded game; 1 - just started game; 0 otherwise
+	int firstCall;
+	int autosaveCount;
+	static const int SAVES_COUNT = 5;
+
+	std::pair<const CCreatureSet *, const CCreatureSet *> lastBattleArmies;
+	bool allowBattleReplay = false;
+	std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
+	const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr)
 
 
-	std::shared_ptr<Environment> env;
 	ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation
 	ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation
 	int3 destinationTeleportPos;
 	int3 destinationTeleportPos;
 
 
+public: // TODO: make private
+	std::shared_ptr<Environment> env;
+
+	std::unique_ptr<PlayerLocalState> localState;
+
 	//minor interfaces
 	//minor interfaces
 	CondSh<bool> *showingDialog; //indicates if dialog box is displayed
 	CondSh<bool> *showingDialog; //indicates if dialog box is displayed
 
 
 	static boost::recursive_mutex *pim;
 	static boost::recursive_mutex *pim;
 	bool makingTurn; //if player is already making his turn
 	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;
 
 
 	CCastleInterface * castleInt; //nullptr if castle window isn't opened
 	CCastleInterface * castleInt; //nullptr if castle window isn't opened
 	static std::shared_ptr<BattleInterface> battleInt; //nullptr if no battle
 	static std::shared_ptr<BattleInterface> battleInt; //nullptr if no battle
 	CInGameConsole * cingconsole;
 	CInGameConsole * cingconsole;
 
 
 	std::shared_ptr<CCallback> cb; //to communicate with engine
 	std::shared_ptr<CCallback> cb; //to communicate with engine
-	const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr)
-
-	std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
-
-	std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
-	std::vector<const CGTownInstance *> towns; //our towns on the adventure map
-	std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
 
 
 	//During battle is quick combat mode is used
 	//During battle is quick combat mode is used
 	std::shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
 	std::shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
 	bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
 	bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
-	bool allowBattleReplay = false;
-	std::pair<const CCreatureSet *, const CCreatureSet *> lastBattleArmies;
 
 
-	struct SpellbookLastSetting
-	{
-		int spellbookLastPageBattle, spellbokLastPageAdvmap; //on which page we left spellbook
-		int spellbookLastTabBattle, spellbookLastTabAdvmap; //on which page we left spellbook
-
-		SpellbookLastSetting();
-		template <typename Handler> void serialize( Handler &h, const int version )
-		{
-			h & spellbookLastPageBattle;
-			h & spellbokLastPageAdvmap;
-			h & spellbookLastTabBattle;
-			h & spellbookLastTabAdvmap;
-		}
-	} spellbookSettings;
+protected: // Call-ins from server, should not be called directly, but only via GameInterface
 
 
 	void update() override;
 	void update() override;
-	void initializeHeroTownList();
-	int getLastIndex(std::string namePrefix);
+	void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
 
 
-	//overridden funcs from CGameInterface
 	void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
 	void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
-
 	void buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) override; //what: 1 - built, 2 - demolished
 	void buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) override; //what: 1 - built, 2 - demolished
 
 
 	void artifactPut(const ArtifactLocation &al) override;
 	void artifactPut(const ArtifactLocation &al) override;
@@ -172,22 +126,16 @@ public:
 	void receivedResource() override;
 	void receivedResource() override;
 	void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
 	void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
 	void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override;
 	void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override;
-	void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
 	void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
 	void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
-	void showPuzzleMap() override;
-	void viewWorldMap() override;
 	void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) override;
 	void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) override;
 	void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor) override;
 	void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor) override;
 	void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
 	void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
-	void showTavernWindow(const CGObjectInstance *townOrTavern) override;
-	void showThievesGuildWindow (const CGObjectInstance * obj) override;
-	void showQuestLog() override;
 	void advmapSpellCast(const CGHeroInstance * caster, int spellID) override; //called when a hero casts a spell
 	void advmapSpellCast(const CGHeroInstance * caster, int spellID) override; //called when a hero casts a spell
-	void tileHidden(const std::unordered_set<int3, ShashInt3> &pos) override; //called when given tiles become hidden under fog of war
-	void tileRevealed(const std::unordered_set<int3, ShashInt3> &pos) override; //called when fog of war disappears from given tiles
+	void tileHidden(const std::unordered_set<int3> &pos) override; //called when given tiles become hidden under fog of war
+	void tileRevealed(const std::unordered_set<int3> &pos) override; //called when fog of war disappears from given tiles
 	void newObject(const CGObjectInstance * obj) override;
 	void newObject(const CGObjectInstance * obj) override;
 	void availableArtifactsChanged(const CGBlackMarket *bm = nullptr) override; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
 	void availableArtifactsChanged(const CGBlackMarket *bm = nullptr) override; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
 	void yourTurn() override;
 	void yourTurn() override;
@@ -230,21 +178,23 @@ public:
 	void yourTacticPhase(int distance) override;
 	void yourTacticPhase(int distance) override;
 	void forceEndTacticPhase() override;
 	void forceEndTacticPhase() override;
 
 
-	//-------------//
+public: // public interface for use by client via LOCPLINT access
+
+	// part of GameInterface that is also used by client code
+	void showPuzzleMap() override;
+	void viewWorldMap() override;
+	void showQuestLog() override;
+	void showThievesGuildWindow (const CGObjectInstance * obj) override;
+	void showTavernWindow(const CGObjectInstance *townOrTavern) override;
+	void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
+
+	void showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2);
 	void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<bool()> onYes);
 	void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<bool()> onYes);
-	void garrisonsChanged(std::vector<const CGObjectInstance *> objs);
-	void garrisonChanged(const CGObjectInstance * obj);
-	void heroKilled(const CGHeroInstance* hero);
 	void waitWhileDialog(bool unlockPim = true);
 	void waitWhileDialog(bool unlockPim = true);
 	void waitForAllDialogs(bool unlockPim = true);
 	void waitForAllDialogs(bool unlockPim = true);
-	void redrawHeroWin(const CGHeroInstance * hero);
 	void openTownWindow(const CGTownInstance * town); //shows townscreen
 	void openTownWindow(const CGTownInstance * town); //shows townscreen
 	void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
 	void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
-	void updateInfo(const CGObjectInstance * specific);
-	void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
-	void activateForSpectator(); // TODO: spectator probably need own player interface class
 
 
-	// show dialogs
 	void showInfoDialog(const std::string &text, std::shared_ptr<CComponent> component);
 	void showInfoDialog(const std::string &text, std::shared_ptr<CComponent> component);
 	void showInfoDialog(const std::string &text, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>(), int soundID = 0);
 	void showInfoDialog(const std::string &text, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>(), int soundID = 0);
 	void showInfoDialogAndWait(std::vector<Component> & components, const MetaString & text);
 	void showInfoDialogAndWait(std::vector<Component> & components, const MetaString & text);
@@ -253,10 +203,8 @@ public:
 	void stopMovement();
 	void stopMovement();
 	void moveHero(const CGHeroInstance *h, const CGPath& path);
 	void moveHero(const CGHeroInstance *h, const CGPath& path);
 
 
-	void acceptTurn(); //used during hot seat after your turn message is close
 	void tryDiggging(const CGHeroInstance *h);
 	void tryDiggging(const CGHeroInstance *h);
 	void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard;
 	void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard;
-	void requestReturningToMainMenu(bool won);
 	void proposeLoadingGame();
 	void proposeLoadingGame();
 
 
 	///returns true if all events are processed internally
 	///returns true if all events are processed internally
@@ -266,11 +214,6 @@ public:
 	~CPlayerInterface();
 	~CPlayerInterface();
 
 
 private:
 private:
-
-	template <typename Handler> void serializeTempl(Handler &h, const int version);
-
-private:
-
 	struct IgnoreEvents
 	struct IgnoreEvents
 	{
 	{
 		CPlayerInterface & owner;
 		CPlayerInterface & owner;
@@ -285,14 +228,18 @@ private:
 
 
 	};
 	};
 
 
-
-
-	bool duringMovement;
-	bool ignoreEvents;
-	size_t numOfMovedArts;
-
+	void heroKilled(const CGHeroInstance* hero);
+	void garrisonsChanged(std::vector<const CGObjectInstance *> objs);
+	void requestReturningToMainMenu(bool won);
+	void acceptTurn(); //used during hot seat after your turn message is close
+	void initializeHeroTownList();
+	int getLastIndex(std::string namePrefix);
 	void doMoveHero(const CGHeroInstance *h, CGPath path);
 	void doMoveHero(const CGHeroInstance *h, CGPath path);
 	void setMovementStatus(bool value);
 	void setMovementStatus(bool value);
+
+	/// Performs autosave, if needed according to settings
+	void performAutosave();
 };
 };
 
 
+/// Provides global access to instance of interface of currently active player
 extern CPlayerInterface * LOCPLINT;
 extern CPlayerInterface * LOCPLINT;

+ 1 - 1
client/Client.cpp

@@ -15,7 +15,7 @@
 #include "CPlayerInterface.h"
 #include "CPlayerInterface.h"
 #include "CServerHandler.h"
 #include "CServerHandler.h"
 #include "ClientNetPackVisitors.h"
 #include "ClientNetPackVisitors.h"
-#include "adventureMap/CAdvMapInt.h"
+#include "adventureMap/CAdventureMapInterface.h"
 #include "battle/BattleInterface.h"
 #include "battle/BattleInterface.h"
 #include "gui/CGuiHandler.h"
 #include "gui/CGuiHandler.h"
 #include "mapView/mapHandler.h"
 #include "mapView/mapHandler.h"

+ 1 - 1
client/Client.h

@@ -235,7 +235,7 @@ public:
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};
 
 
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override {}
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override {}
-	void changeFogOfWar(std::unordered_set<int3, ShashInt3> & tiles, PlayerColor player, bool hide) override {}
+	void changeFogOfWar(std::unordered_set<int3> & tiles, PlayerColor player, bool hide) override {}
 
 
 	void setObjProperty(ObjectInstanceID objid, int prop, si64 val) override {}
 	void setObjProperty(ObjectInstanceID objid, int prop, si64 val) override {}
 
 

+ 6 - 7
client/ClientCommandManager.cpp

@@ -8,13 +8,12 @@
  *
  *
  */
  */
 #include "StdInc.h"
 #include "StdInc.h"
-
 #include "ClientCommandManager.h"
 #include "ClientCommandManager.h"
 
 
 #include "Client.h"
 #include "Client.h"
 #include "adventureMap/CInGameConsole.h"
 #include "adventureMap/CInGameConsole.h"
-#include "adventureMap/CAdvMapInt.h"
 #include "CPlayerInterface.h"
 #include "CPlayerInterface.h"
+#include "PlayerLocalState.h"
 #include "CServerHandler.h"
 #include "CServerHandler.h"
 #include "gui/CGuiHandler.h"
 #include "gui/CGuiHandler.h"
 #include "../lib/NetPacks.h"
 #include "../lib/NetPacks.h"
@@ -387,12 +386,12 @@ void ClientCommandManager::handleBonusesCommand(std::istringstream & singleWordB
 		ss << b;
 		ss << b;
 		return ss.str();
 		return ss.str();
 	};
 	};
-	printCommandMessage("Bonuses of " + adventureInt->curArmy()->getObjectName() + "\n");
-	printCommandMessage(format(adventureInt->curArmy()->getBonusList()) + "\n");
+		printCommandMessage("Bonuses of " + LOCPLINT->localState->getCurrentArmy()->getObjectName() + "\n");
+		printCommandMessage(format(LOCPLINT->localState->getCurrentArmy()->getBonusList()) + "\n");
 
 
 	printCommandMessage("\nInherited bonuses:\n");
 	printCommandMessage("\nInherited bonuses:\n");
 	TCNodes parents;
 	TCNodes parents;
-	adventureInt->curArmy()->getParents(parents);
+		LOCPLINT->localState->getCurrentArmy()->getParents(parents);
 	for(const CBonusSystemNode *parent : parents)
 	for(const CBonusSystemNode *parent : parents)
 	{
 	{
 		printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
 		printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
@@ -416,7 +415,7 @@ void ClientCommandManager::handleTellCommand(std::istringstream& singleWordBuffe
 
 
 void ClientCommandManager::handleMpCommand()
 void ClientCommandManager::handleMpCommand()
 {
 {
-	if(const CGHeroInstance* h = adventureInt->curHero())
+	if(const CGHeroInstance* h = LOCPLINT->localState->getCurrentHero())
 		printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n");
 		printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n");
 }
 }
 
 
@@ -602,7 +601,7 @@ void ClientCommandManager::processCommand(const std::string & message, bool call
 	else if(commandName == "tell")
 	else if(commandName == "tell")
 		handleTellCommand(singleWordBuffer);
 		handleTellCommand(singleWordBuffer);
 
 
-	else if(commandName == "mp" && adventureInt)
+	else if(commandName == "mp" && LOCPLINT)
 		handleMpCommand();
 		handleMpCommand();
 
 
 	else if (commandName == "set")
 	else if (commandName == "set")

+ 268 - 0
client/PlayerLocalState.cpp

@@ -0,0 +1,268 @@
+/*
+ * PlayerLocalState.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "PlayerLocalState.h"
+
+#include "../CCallback.h"
+#include "../lib/CPathfinder.h"
+#include "../lib/mapObjects/CGHeroInstance.h"
+#include "../lib/mapObjects/CGTownInstance.h"
+#include "CPlayerInterface.h"
+#include "adventureMap/CAdventureMapInterface.h"
+
+PlayerLocalState::PlayerLocalState(CPlayerInterface & owner)
+	: owner(owner)
+	, currentSelection(nullptr)
+{
+}
+
+void PlayerLocalState::saveHeroPaths(std::map<const CGHeroInstance *, int3> & pathsMap)
+{
+	for(auto & p : paths)
+	{
+		if(p.second.nodes.size())
+			pathsMap[p.first] = p.second.endPos();
+		else
+			logGlobal->debug("%s has assigned an empty path! Ignoring it...", p.first->getNameTranslated());
+	}
+}
+
+void PlayerLocalState::loadHeroPaths(std::map<const CGHeroInstance *, int3> & pathsMap)
+{
+	if(owner.cb)
+	{
+		for(auto & p : pathsMap)
+		{
+			CGPath path;
+			owner.cb->getPathsInfo(p.first)->getPath(path, p.second);
+			paths[p.first] = path;
+			logGlobal->trace("Restored path for hero %s leading to %s with %d nodes", p.first->nodeName(), p.second.toString(), path.nodes.size());
+		}
+	}
+}
+
+void PlayerLocalState::setPath(const CGHeroInstance * h, const CGPath & path)
+{
+	paths[h] = path;
+}
+
+const CGPath & PlayerLocalState::getPath(const CGHeroInstance * h) const
+{
+	assert(hasPath(h));
+	return paths.at(h);
+}
+
+bool PlayerLocalState::hasPath(const CGHeroInstance * h) const
+{
+	return paths.count(h) > 0;
+}
+
+bool PlayerLocalState::setPath(const CGHeroInstance * h, const int3 & destination)
+{
+	CGPath path;
+	if(!owner.cb->getPathsInfo(h)->getPath(path, destination))
+		return false;
+
+	setPath(h, path);
+	return true;
+}
+
+void PlayerLocalState::removeLastNode(const CGHeroInstance * h)
+{
+	assert(hasPath(h));
+	if(!hasPath(h))
+		return;
+
+	auto & path = paths[h];
+	path.nodes.pop_back();
+	if(path.nodes.size() < 2) //if it was the last one, remove entire path and path with only one tile is not a real path
+		erasePath(h);
+}
+
+void PlayerLocalState::erasePath(const CGHeroInstance * h)
+{
+	paths.erase(h);
+	adventureInt->onHeroChanged(h);
+}
+
+void PlayerLocalState::verifyPath(const CGHeroInstance * h)
+{
+	if(!hasPath(h))
+		return;
+	setPath(h, getPath(h).endPos());
+}
+
+const CGHeroInstance * PlayerLocalState::getCurrentHero() const
+{
+	if(currentSelection && currentSelection->ID == Obj::HERO)
+		return dynamic_cast<const CGHeroInstance *>(currentSelection);
+	else
+		return nullptr;
+}
+
+const CGHeroInstance * PlayerLocalState::getNextWanderingHero(const CGHeroInstance * currentHero)
+{
+	bool currentHeroFound = false;
+	const CGHeroInstance * firstSuitable = nullptr;
+	const CGHeroInstance * nextSuitable = nullptr;
+
+	for(const auto * hero : getWanderingHeroes())
+	{
+		if (hero == currentHero)
+		{
+			currentHeroFound = true;
+			continue;
+		}
+
+		if (isHeroSleeping(hero))
+			continue;
+
+		if (hero->movement == 0)
+			continue;
+
+		if (!firstSuitable)
+			firstSuitable = hero;
+
+		if (!nextSuitable && currentHeroFound)
+			nextSuitable = hero;
+	}
+
+	// if we found suitable hero after currently selected hero -> return this hero
+	if (nextSuitable)
+		return nextSuitable;
+
+	// othervice -> loop over and return first suitable hero in the list (or null if none)
+	return firstSuitable;
+}
+
+const CGTownInstance * PlayerLocalState::getCurrentTown() const
+{
+	if(currentSelection && currentSelection->ID == Obj::TOWN)
+		return dynamic_cast<const CGTownInstance *>(currentSelection);
+	else
+		return nullptr;
+}
+
+const CArmedInstance * PlayerLocalState::getCurrentArmy() const
+{
+	if(currentSelection)
+		return dynamic_cast<const CArmedInstance *>(currentSelection);
+	else
+		return nullptr;
+}
+
+void PlayerLocalState::setSelection(const CArmedInstance * selection)
+{
+	if (currentSelection == selection)
+		return;
+
+	currentSelection = selection;
+
+	if (selection)
+		adventureInt->onSelectionChanged(selection);
+}
+
+bool PlayerLocalState::isHeroSleeping(const CGHeroInstance * hero) const
+{
+	return vstd::contains(sleepingHeroes, hero);
+}
+
+void PlayerLocalState::setHeroAsleep(const CGHeroInstance * hero)
+{
+	assert(hero);
+	assert(vstd::contains(wanderingHeroes, hero));
+	assert(!vstd::contains(sleepingHeroes, hero));
+
+	sleepingHeroes.push_back(hero);
+}
+
+void PlayerLocalState::setHeroAwaken(const CGHeroInstance * hero)
+{
+	assert(hero);
+	assert(vstd::contains(wanderingHeroes, hero));
+	assert(vstd::contains(sleepingHeroes, hero));
+
+	vstd::erase(sleepingHeroes, hero);
+}
+
+const std::vector<const CGHeroInstance *> & PlayerLocalState::getWanderingHeroes()
+{
+	return wanderingHeroes;
+}
+
+const CGHeroInstance * PlayerLocalState::getWanderingHero(size_t index)
+{
+	if(index < wanderingHeroes.size())
+		return wanderingHeroes[index];
+	return nullptr;
+}
+
+void PlayerLocalState::addWanderingHero(const CGHeroInstance * hero)
+{
+	assert(hero);
+	assert(!vstd::contains(wanderingHeroes, hero));
+	wanderingHeroes.push_back(hero);
+}
+
+void PlayerLocalState::removeWanderingHero(const CGHeroInstance * hero)
+{
+	assert(hero);
+	assert(vstd::contains(wanderingHeroes, hero));
+
+	if (hero == currentSelection)
+	{
+		auto const * nextHero = getNextWanderingHero(hero);
+		setSelection(nextHero);
+	}
+
+	vstd::erase(wanderingHeroes, hero);
+	vstd::erase(sleepingHeroes, hero);
+
+	if (currentSelection == nullptr && !wanderingHeroes.empty())
+		setSelection(wanderingHeroes.front());
+
+	if (currentSelection == nullptr && !ownedTowns.empty())
+		setSelection(ownedTowns.front());
+}
+
+const std::vector<const CGTownInstance *> & PlayerLocalState::getOwnedTowns()
+{
+	return ownedTowns;
+}
+
+const CGTownInstance * PlayerLocalState::getOwnedTown(size_t index)
+{
+	if(index < ownedTowns.size())
+		return ownedTowns[index];
+	return nullptr;
+}
+
+void PlayerLocalState::addOwnedTown(const CGTownInstance * town)
+{
+	assert(town);
+	assert(!vstd::contains(ownedTowns, town));
+	ownedTowns.push_back(town);
+}
+
+void PlayerLocalState::removeOwnedTown(const CGTownInstance * town)
+{
+	assert(town);
+	assert(vstd::contains(ownedTowns, town));
+	vstd::erase(ownedTowns, town);
+
+	if (town == currentSelection)
+		setSelection(nullptr);
+
+	if (currentSelection == nullptr && !wanderingHeroes.empty())
+		setSelection(wanderingHeroes.front());
+
+	if (currentSelection == nullptr && !ownedTowns.empty())
+		setSelection(ownedTowns.front());
+}

+ 111 - 0
client/PlayerLocalState.h

@@ -0,0 +1,111 @@
+/*
+ * PlayerLocalState.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class CGHeroInstance;
+class CGTownInstance;
+class CArmedInstance;
+struct CGPath;
+class int3;
+
+VCMI_LIB_NAMESPACE_END
+
+class CPlayerInterface;
+
+/// Class that contains potentially serializeable state of a local player
+class PlayerLocalState
+{
+	CPlayerInterface & owner;
+
+	/// Currently selected object, can be town, hero or null
+	const CArmedInstance * currentSelection;
+
+	std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
+	std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
+	std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
+	std::vector<const CGTownInstance *> ownedTowns; //our towns on the adventure map
+
+	void saveHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
+	void loadHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
+
+public:
+	struct SpellbookLastSetting
+	{
+		//on which page we left spellbook
+		int spellbookLastPageBattle = 0;
+		int spellbokLastPageAdvmap = 0;
+		int spellbookLastTabBattle = 4;
+		int spellbookLastTabAdvmap = 4;
+
+		template<typename Handler>
+		void serialize(Handler & h, const int version)
+		{
+			h & spellbookLastPageBattle;
+			h & spellbokLastPageAdvmap;
+			h & spellbookLastTabBattle;
+			h & spellbookLastTabAdvmap;
+		}
+	} spellbookSettings;
+
+	explicit PlayerLocalState(CPlayerInterface & owner);
+
+	bool isHeroSleeping(const CGHeroInstance * hero) const;
+	void setHeroAsleep(const CGHeroInstance * hero);
+	void setHeroAwaken(const CGHeroInstance * hero);
+
+	const std::vector<const CGTownInstance *> & getOwnedTowns();
+	const CGTownInstance * getOwnedTown(size_t index);
+	void addOwnedTown(const CGTownInstance * hero);
+	void removeOwnedTown(const CGTownInstance * hero);
+
+	const std::vector<const CGHeroInstance *> & getWanderingHeroes();
+	const CGHeroInstance * getWanderingHero(size_t index);
+	const CGHeroInstance * getNextWanderingHero(const CGHeroInstance * hero);
+	void addWanderingHero(const CGHeroInstance * hero);
+	void removeWanderingHero(const CGHeroInstance * hero);
+
+	void setPath(const CGHeroInstance * h, const CGPath & path);
+	bool setPath(const CGHeroInstance * h, const int3 & destination);
+
+	const CGPath & getPath(const CGHeroInstance * h) const;
+	bool hasPath(const CGHeroInstance * h) const;
+
+	void removeLastNode(const CGHeroInstance * h);
+	void erasePath(const CGHeroInstance * h);
+	void verifyPath(const CGHeroInstance * h);
+
+	/// Returns currently selected object
+	const CGHeroInstance * getCurrentHero() const;
+	const CGTownInstance * getCurrentTown() const;
+	const CArmedInstance * getCurrentArmy() const;
+
+	/// Changes currently selected object
+	void setSelection(const CArmedInstance *sel);
+
+	template<typename Handler>
+	void serialize(Handler & h, int version)
+	{
+		//WARNING: this code is broken and not used. See CClient::loadGame
+		std::map<const CGHeroInstance *, int3> pathsMap; //hero -> dest
+		if(h.saving)
+			saveHeroPaths(pathsMap);
+
+		h & pathsMap;
+
+		if(!h.saving)
+			loadHeroPaths(pathsMap);
+
+		h & ownedTowns;
+		h & wanderingHeroes;
+		h & sleepingHeroes;
+	}
+};

File diff suppressed because it is too large
+ 270 - 354
client/adventureMap/CAdventureMapInterface.cpp


+ 68 - 64
client/adventureMap/CAdvMapInt.h → client/adventureMap/CAdventureMapInterface.h

@@ -11,9 +11,6 @@
 
 
 #include "../gui/CIntObject.h"
 #include "../gui/CIntObject.h"
 
 
-#include "../../lib/int3.h"
-#include "../../lib/GameConstants.h"
-
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
 class CGObjectInstance;
 class CGObjectInstance;
@@ -23,6 +20,8 @@ class CArmedInstance;
 class IShipyard;
 class IShipyard;
 struct CGPathNode;
 struct CGPathNode;
 struct ObjectPosInfo;
 struct ObjectPosInfo;
+struct Component;
+class int3;
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END
 
 
@@ -43,38 +42,28 @@ class MapAudioPlayer;
 
 
 struct MapDrawingInfo;
 struct MapDrawingInfo;
 
 
-enum class EAdvMapMode
-{
-	NORMAL,
-	WORLD_VIEW
-};
-
 /// That's a huge class which handles general adventure map actions and
 /// That's a huge class which handles general adventure map actions and
 /// shows the right menu(questlog, spellbook, end turn,..) from where you
 /// shows the right menu(questlog, spellbook, end turn,..) from where you
 /// can get to the towns and heroes.
 /// can get to the towns and heroes.
-class CAdvMapInt : public CIntObject
+class CAdventureMapInterface : public CIntObject
 {
 {
-	//TODO: remove
-	friend class CPlayerInterface;
-
 private:
 private:
-	enum EDirections {LEFT=1, RIGHT=2, UP=4, DOWN=8};
-	enum EGameStates {NA, INGAME, WAITING};
+	enum class EGameState
+	{
+		NOT_INITIALIZED,
+		HOTSEAT_WAIT,
+		MAKING_TURN,
+		ENEMY_TURN,
+		WORLD_VIEW
+	};
 
 
-	EGameStates state;
-	EAdvMapMode mode;
-
-	/// Currently selected object, can be town, hero or null
-	const CArmedInstance *selection;
+	EGameState state;
 
 
 	/// currently acting player
 	/// currently acting player
-	PlayerColor player;
-
-	bool duringAITurn;
+	PlayerColor currentPlayerID;
 
 
 	/// uses EDirections enum
 	/// uses EDirections enum
-	ui8 scrollingDir;
-	bool scrollingState;
+	bool scrollingCursorSet;
 
 
 	const CSpell *spellBeingCasted; //nullptr if none
 	const CSpell *spellBeingCasted; //nullptr if none
 
 
@@ -127,15 +116,13 @@ private:
 	void fnextHero();
 	void fnextHero();
 	void fendTurn();
 	void fendTurn();
 
 
-	void setScrollingCursor(ui8 direction) const;
-	void selectionChanged();
 	bool isActive();
 	bool isActive();
 	void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
 	void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
 
 
 	const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
 	const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
-	//button updates
-	void updateSleepWake(const CGHeroInstance *h);
-	void updateSpellbook(const CGHeroInstance *h);
+
+	// update locked state of buttons
+	void updateButtons();
 
 
 	void handleMapScrollingUpdate();
 	void handleMapScrollingUpdate();
 
 
@@ -145,9 +132,15 @@ private:
 
 
 	std::optional<Point> keyToMoveDirection(const SDL_Keycode & key);
 	std::optional<Point> keyToMoveDirection(const SDL_Keycode & key);
 
 
-public:
-	CAdvMapInt();
+	void endingTurn();
 
 
+	/// exits currently opened world view mode and returns to normal map
+	void exitWorldView();
+	void exitCastingMode();
+	void leaveCastingMode(const int3 & castTarget);
+	void abortCastingMode();
+
+protected:
 	// CIntObject interface implementation
 	// CIntObject interface implementation
 
 
 	void activate() override;
 	void activate() override;
@@ -157,58 +150,69 @@ public:
 	void showAll(SDL_Surface * to) override;
 	void showAll(SDL_Surface * to) override;
 
 
 	void keyPressed(const SDL_Keycode & key) override;
 	void keyPressed(const SDL_Keycode & key) override;
-	void keyReleased(const SDL_Keycode & key) override;
-	void mouseMoved (const Point & cursorPosition) override;
 
 
-	// public interface
+public:
+	CAdventureMapInterface();
 
 
-	/// called by MapView whenever currently visible area changes
-	/// visibleArea describen now visible map section measured in tiles
-	void onMapViewMoved(const Rect & visibleArea, int mapLevel);
+	/// Called by PlayerInterface when specified player is ready to start his turn
+	void onHotseatWaitStarted(PlayerColor playerID);
+
+	/// Called by PlayerInterface when AI or remote human player starts his turn
+	void onEnemyTurnStarted(PlayerColor playerID);
+
+	/// Called by PlayerInterface when local human player starts his turn
+	void onPlayerTurnStarted(PlayerColor playerID);
 
 
-	/// Called when map audio should be paused, e.g. on combat or town scren access
+	/// Called by PlayerInterface when interface should be switched to specified player without starting turn
+	void onCurrentPlayerChanged(PlayerColor playerID);
+
+	/// Called by PlayerInterface when specific map tile changed and must be updated on minimap
+	void onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions);
+
+	/// Called by PlayerInterface when hero starts movement
+	void onHeroMovementStarted(const CGHeroInstance * hero);
+
+	/// Called by PlayerInterface when hero state changed and hero list must be updated
+	void onHeroChanged(const CGHeroInstance * hero);
+
+	/// Called by PlayerInterface when town state changed and town list must be updated
+	void onTownChanged(const CGTownInstance * town);
+
+	/// Called when currently selected object changes
+	void onSelectionChanged(const CArmedInstance *sel);
+
+	/// Called when map audio should be paused, e.g. on combat or town screen access
 	void onAudioPaused();
 	void onAudioPaused();
 
 
 	/// Called when map audio should be resume, opposite to onPaused
 	/// Called when map audio should be resume, opposite to onPaused
 	void onAudioResumed();
 	void onAudioResumed();
 
 
-	void select(const CArmedInstance *sel, bool centerView = true);
+	/// Requests to display provided information inside infobox
+	void showInfoBoxMessage(const std::vector<Component> & components, std::string message, int timer);
+
+	/// Changes position on map to center selected location
 	void centerOnTile(int3 on);
 	void centerOnTile(int3 on);
 	void centerOnObject(const CGObjectInstance *obj);
 	void centerOnObject(const CGObjectInstance *obj);
 
 
-	bool isHeroSleeping(const CGHeroInstance *hero);
-	void setHeroSleeping(const CGHeroInstance *hero, bool sleep);
-	int getNextHeroIndex(int startIndex); //for Next Hero button - cycles awake heroes with movement only
-
-	void setPlayer(PlayerColor Player);
-	void startHotSeatWait(PlayerColor Player);
-	void startTurn();
-	void initializeNewTurn();
-	void endingTurn();
-	void aiTurnStarted();
-
-	void quickCombatLock(); //should be called when quick battle started
-	void quickCombatUnlock();
+	/// called by MapView whenever currently visible area changes
+	/// visibleArea describes now visible map section measured in tiles
+	void onMapViewMoved(const Rect & visibleArea, int mapLevel);
 
 
+	/// called by MapView whenever tile is clicked
 	void onTileLeftClicked(const int3 & mapPos);
 	void onTileLeftClicked(const int3 & mapPos);
+
+	/// called by MapView whenever tile is hovered
 	void onTileHovered(const int3 & mapPos);
 	void onTileHovered(const int3 & mapPos);
+
+	/// called by MapView whenever tile is clicked
 	void onTileRightClicked(const int3 & mapPos);
 	void onTileRightClicked(const int3 & mapPos);
 
 
+	/// called by spell window when spell to cast has been selected
 	void enterCastingMode(const CSpell * sp);
 	void enterCastingMode(const CSpell * sp);
-	void leaveCastingMode(bool cast = false, int3 dest = int3(-1, -1, -1));
-	const CGHeroInstance * curHero() const;
-	const CGTownInstance * curTown() const;
-	const CArmedInstance * curArmy() const;
-
-	void updateMoveHero(const CGHeroInstance *h, tribool hasPath = boost::logic::indeterminate);
-	void updateNextHero(const CGHeroInstance *h);
 
 
 	/// returns area of screen covered by terrain (main game area)
 	/// returns area of screen covered by terrain (main game area)
 	Rect terrainAreaPixels() const;
 	Rect terrainAreaPixels() const;
 
 
-	/// exits currently opened world view mode and returns to normal map
-	void exitWorldView();
-
 	/// opens world view at default scale
 	/// opens world view at default scale
 	void openWorldView();
 	void openWorldView();
 
 
@@ -219,4 +223,4 @@ public:
 	void openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain);
 	void openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain);
 };
 };
 
 
-extern std::shared_ptr<CAdvMapInt> adventureInt;
+extern std::shared_ptr<CAdventureMapInterface> adventureInt;

+ 2 - 3
client/adventureMap/CAdventureOptions.cpp

@@ -11,10 +11,9 @@
 #include "StdInc.h"
 #include "StdInc.h"
 #include "CAdventureOptions.h"
 #include "CAdventureOptions.h"
 
 
-#include "CAdvMapInt.h"
-
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../lobby/CCampaignInfoScreen.h"
 #include "../lobby/CCampaignInfoScreen.h"
 #include "../lobby/CScenarioInfoScreen.h"
 #include "../lobby/CScenarioInfoScreen.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
@@ -41,7 +40,7 @@ CAdventureOptions::CAdventureOptions()
 	puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
 	puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
 
 
 	dig = std::make_shared<CButton>(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_d);
 	dig = std::make_shared<CButton>(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_d);
-	if(const CGHeroInstance *h = adventureInt->curHero())
+	if(const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero())
 		dig->addCallback(std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h));
 		dig->addCallback(std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h));
 	else
 	else
 		dig->block(true);
 		dig->block(true);

+ 2 - 2
client/adventureMap/CInGameConsole.cpp

@@ -14,8 +14,8 @@
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../ClientCommandManager.h"
 #include "../ClientCommandManager.h"
-#include "../adventureMap/CAdvMapInt.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../render/Colors.h"
 #include "../render/Colors.h"
 
 
@@ -259,7 +259,7 @@ void CInGameConsole::endEnteringText(bool processEnteredText)
 			clientCommandThread.detach();
 			clientCommandThread.detach();
 		}
 		}
 		else
 		else
-			LOCPLINT->cb->sendMessage(txt, adventureInt->curArmy());
+			LOCPLINT->cb->sendMessage(txt, LOCPLINT->localState->getCurrentArmy());
 	}
 	}
 	enteredText.clear();
 	enteredText.clear();
 
 

+ 7 - 6
client/adventureMap/CInfoBar.cpp

@@ -11,7 +11,7 @@
 #include "StdInc.h"
 #include "StdInc.h"
 #include "CInfoBar.h"
 #include "CInfoBar.h"
 
 
-#include "CAdvMapInt.h"
+#include "CAdventureMapInterface.h"
 
 
 #include "../widgets/CComponent.h"
 #include "../widgets/CComponent.h"
 #include "../widgets/Images.h"
 #include "../widgets/Images.h"
@@ -22,6 +22,7 @@
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 
 
 #include "../../CCallback.h"
 #include "../../CCallback.h"
@@ -114,7 +115,7 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo()
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	//get amount of halls of each level
 	//get amount of halls of each level
 	std::vector<int> halls(4, 0);
 	std::vector<int> halls(4, 0);
-	for(auto town : LOCPLINT->towns)
+	for(auto town : LOCPLINT->localState->getOwnedTowns())
 	{
 	{
 		int hallLevel = town->hallLevel();
 		int hallLevel = town->hallLevel();
 		//negative value means no village hall, unlikely but possible
 		//negative value means no village hall, unlikely but possible
@@ -237,15 +238,15 @@ void CInfoBar::reset()
 void CInfoBar::showSelection()
 void CInfoBar::showSelection()
 {
 {
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
-	if(adventureInt->curHero())
+	if(LOCPLINT->localState->getCurrentHero())
 	{
 	{
-		showHeroSelection(adventureInt->curHero());
+		showHeroSelection(LOCPLINT->localState->getCurrentHero());
 		return;
 		return;
 	}
 	}
 
 
-	if(adventureInt->curTown())
+	if(LOCPLINT->localState->getCurrentTown())
 	{
 	{
-		showTownSelection(adventureInt->curTown());
+		showTownSelection(LOCPLINT->localState->getCurrentTown());
 		return;
 		return;
 	}
 	}
 
 

+ 21 - 20
client/adventureMap/CList.cpp

@@ -11,13 +11,14 @@
 #include "StdInc.h"
 #include "StdInc.h"
 #include "CList.h"
 #include "CList.h"
 
 
-#include "CAdvMapInt.h"
+#include "CAdventureMapInterface.h"
 
 
 #include "../widgets/Images.h"
 #include "../widgets/Images.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/Buttons.h"
 #include "../windows/InfoWindows.h"
 #include "../windows/InfoWindows.h"
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 
 
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
@@ -203,8 +204,8 @@ std::shared_ptr<CIntObject> CHeroList::CHeroItem::genSelection()
 
 
 void CHeroList::CHeroItem::select(bool on)
 void CHeroList::CHeroItem::select(bool on)
 {
 {
-	if(on && adventureInt->curHero() != hero)
-		adventureInt->select(hero);
+	if(on)
+		LOCPLINT->localState->setSelection(hero);
 }
 }
 
 
 void CHeroList::CHeroItem::open()
 void CHeroList::CHeroItem::open()
@@ -224,19 +225,19 @@ std::string CHeroList::CHeroItem::getHoverText()
 
 
 std::shared_ptr<CIntObject> CHeroList::createHeroItem(size_t index)
 std::shared_ptr<CIntObject> CHeroList::createHeroItem(size_t index)
 {
 {
-	if (LOCPLINT->wanderingHeroes.size() > index)
-		return std::make_shared<CHeroItem>(this, LOCPLINT->wanderingHeroes[index]);
+	if (LOCPLINT->localState->getWanderingHeroes().size() > index)
+		return std::make_shared<CHeroItem>(this, LOCPLINT->localState->getWanderingHero(index));
 	return std::make_shared<CEmptyHeroItem>();
 	return std::make_shared<CEmptyHeroItem>();
 }
 }
 
 
 CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
 CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
-	CList(size, position, btnUp, btnDown, LOCPLINT->wanderingHeroes.size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
+	CList(size, position, btnUp, btnDown, LOCPLINT->localState->getWanderingHeroes().size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
 {
 {
 }
 }
 
 
 void CHeroList::select(const CGHeroInstance * hero)
 void CHeroList::select(const CGHeroInstance * hero)
 {
 {
-	selectIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
+	selectIndex(vstd::find_pos(LOCPLINT->localState->getWanderingHeroes(), hero));
 }
 }
 
 
 void CHeroList::update(const CGHeroInstance * hero)
 void CHeroList::update(const CGHeroInstance * hero)
@@ -245,7 +246,7 @@ void CHeroList::update(const CGHeroInstance * hero)
 	for(auto & elem : listBox->getItems())
 	for(auto & elem : listBox->getItems())
 	{
 	{
 		auto item = std::dynamic_pointer_cast<CHeroItem>(elem);
 		auto item = std::dynamic_pointer_cast<CHeroItem>(elem);
-		if(item && item->hero == hero && vstd::contains(LOCPLINT->wanderingHeroes, hero))
+		if(item && item->hero == hero && vstd::contains(LOCPLINT->localState->getWanderingHeroes(), hero))
 		{
 		{
 			item->update();
 			item->update();
 			return;
 			return;
@@ -253,17 +254,17 @@ void CHeroList::update(const CGHeroInstance * hero)
 	}
 	}
 	//simplest solution for now: reset list and restore selection
 	//simplest solution for now: reset list and restore selection
 
 
-	listBox->resize(LOCPLINT->wanderingHeroes.size());
-	if (adventureInt->curHero())
-		select(adventureInt->curHero());
+	listBox->resize(LOCPLINT->localState->getWanderingHeroes().size());
+	if (LOCPLINT->localState->getCurrentHero())
+		select(LOCPLINT->localState->getCurrentHero());
 
 
 	CList::update();
 	CList::update();
 }
 }
 
 
 std::shared_ptr<CIntObject> CTownList::createTownItem(size_t index)
 std::shared_ptr<CIntObject> CTownList::createTownItem(size_t index)
 {
 {
-	if (LOCPLINT->towns.size() > index)
-		return std::make_shared<CTownItem>(this, LOCPLINT->towns[index]);
+	if (LOCPLINT->localState->getOwnedTowns().size() > index)
+		return std::make_shared<CTownItem>(this, LOCPLINT->localState->getOwnedTown(index));
 	return std::make_shared<CAnimImage>("ITPA", 0);
 	return std::make_shared<CAnimImage>("ITPA", 0);
 }
 }
 
 
@@ -292,8 +293,8 @@ void CTownList::CTownItem::update()
 
 
 void CTownList::CTownItem::select(bool on)
 void CTownList::CTownItem::select(bool on)
 {
 {
-	if (on && adventureInt->curTown() != town)
-		adventureInt->select(town);
+	if(on)
+		LOCPLINT->localState->setSelection(town);
 }
 }
 
 
 void CTownList::CTownItem::open()
 void CTownList::CTownItem::open()
@@ -312,22 +313,22 @@ std::string CTownList::CTownItem::getHoverText()
 }
 }
 
 
 CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
 CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
-	CList(size, position, btnUp, btnDown, LOCPLINT->towns.size(),  306, 307, std::bind(&CTownList::createTownItem, this, _1))
+	CList(size, position, btnUp, btnDown, LOCPLINT->localState->getOwnedTowns().size(),  306, 307, std::bind(&CTownList::createTownItem, this, _1))
 {
 {
 }
 }
 
 
 void CTownList::select(const CGTownInstance * town)
 void CTownList::select(const CGTownInstance * town)
 {
 {
-	selectIndex(vstd::find_pos(LOCPLINT->towns, town));
+	selectIndex(vstd::find_pos(LOCPLINT->localState->getOwnedTowns(), town));
 }
 }
 
 
 void CTownList::update(const CGTownInstance *)
 void CTownList::update(const CGTownInstance *)
 {
 {
 	//simplest solution for now: reset list and restore selection
 	//simplest solution for now: reset list and restore selection
 
 
-	listBox->resize(LOCPLINT->towns.size());
-	if (adventureInt->curTown())
-		select(adventureInt->curTown());
+	listBox->resize(LOCPLINT->localState->getOwnedTowns().size());
+	if (LOCPLINT->localState->getCurrentTown())
+		select(LOCPLINT->localState->getCurrentTown());
 
 
 	CList::update();
 	CList::update();
 }
 }

+ 10 - 4
client/adventureMap/CMinimap.cpp

@@ -11,14 +11,15 @@
 #include "StdInc.h"
 #include "StdInc.h"
 #include "CMinimap.h"
 #include "CMinimap.h"
 
 
-#include "CAdvMapInt.h"
+#include "CAdventureMapInterface.h"
 
 
 #include "../widgets/Images.h"
 #include "../widgets/Images.h"
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../render/Colors.h"
 #include "../render/Colors.h"
-#include "../renderSDL/SDL_PixelAccess.h"
+#include "../renderSDL/SDL_Extensions.h"
+#include "../render/Canvas.h"
 #include "../windows/InfoWindows.h"
 #include "../windows/InfoWindows.h"
 
 
 #include "../../CCallback.h"
 #include "../../CCallback.h"
@@ -27,6 +28,8 @@
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapping/CMapDefines.h"
 #include "../../lib/mapping/CMapDefines.h"
 
 
+#include <SDL_pixels.h>
+
 ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const
 ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const
 {
 {
 	const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
 	const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
@@ -225,10 +228,13 @@ void CMinimap::setAIRadar(bool on)
 	redraw();
 	redraw();
 }
 }
 
 
-void CMinimap::updateTile(const int3 &pos)
+void CMinimap::updateTiles(std::unordered_set<int3> positions)
 {
 {
 	if(minimap)
 	if(minimap)
-		minimap->refreshTile(pos);
+	{
+		for (auto const & tile : positions)
+			minimap->refreshTile(tile);
+	}
 	redraw();
 	redraw();
 }
 }
 
 

+ 2 - 3
client/adventureMap/CMinimap.h

@@ -10,13 +10,12 @@
 #pragma once
 #pragma once
 
 
 #include "../gui/CIntObject.h"
 #include "../gui/CIntObject.h"
-#include "../../lib/GameConstants.h"
-#include "../render/Canvas.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 class ColorRGBA;
 class ColorRGBA;
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END
 
 
+class Canvas;
 class CMinimap;
 class CMinimap;
 
 
 class CMinimapInstance : public CIntObject
 class CMinimapInstance : public CIntObject
@@ -68,6 +67,6 @@ public:
 
 
 	void showAll(SDL_Surface * to) override;
 	void showAll(SDL_Surface * to) override;
 
 
-	void updateTile(const int3 &pos);
+	void updateTiles(std::unordered_set<int3> positions);
 };
 };
 
 

+ 1 - 1
client/adventureMap/MapAudioPlayer.cpp

@@ -157,7 +157,7 @@ void MapAudioPlayer::updateAmbientSounds()
 	};
 	};
 
 
 	int3 pos = currentSelection->getSightCenter();
 	int3 pos = currentSelection->getSightCenter();
-	std::unordered_set<int3, ShashInt3> tiles;
+	std::unordered_set<int3> tiles;
 	LOCPLINT->cb->getVisibleTilesInRange(tiles, pos, CCS->soundh->ambientGetRange(), int3::DIST_CHEBYSHEV);
 	LOCPLINT->cb->getVisibleTilesInRange(tiles, pos, CCS->soundh->ambientGetRange(), int3::DIST_CHEBYSHEV);
 	for(int3 tile : tiles)
 	for(int3 tile : tiles)
 	{
 	{

+ 1 - 1
client/battle/BattleInterface.cpp

@@ -29,7 +29,7 @@
 #include "../gui/CursorHandler.h"
 #include "../gui/CursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../render/Canvas.h"
 #include "../render/Canvas.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 
 
 #include "../../CCallback.h"
 #include "../../CCallback.h"
 #include "../../lib/CStack.h"
 #include "../../lib/CStack.h"

+ 0 - 8
client/battle/BattleStacksController.cpp

@@ -246,14 +246,6 @@ void BattleStacksController::setActiveStack(const CStack *stack)
 
 
 bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const
 bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const
 {
 {
-	BattleHex currentActionTarget;
-	if(owner.curInt->curAction)
-	{
-		auto target = owner.curInt->curAction->getTarget(owner.curInt->cb.get());
-		if(!target.empty())
-			currentActionTarget = target.at(0).hexValue;
-	}
-
 	//do not show box for singular war machines, stacked war machines with box shown are supported as extension feature
 	//do not show box for singular war machines, stacked war machines with box shown are supported as extension feature
 	if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1)
 	if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1)
 		return false;
 		return false;

+ 6 - 6
client/mapView/MapRendererContext.cpp

@@ -17,7 +17,7 @@
 #include "../../CCallback.h"
 #include "../../CCallback.h"
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../PlayerLocalState.h"
 
 
 #include "../../lib/CPathfinder.h"
 #include "../../lib/CPathfinder.h"
 #include "../../lib/Point.h"
 #include "../../lib/Point.h"
@@ -73,9 +73,9 @@ bool MapRendererBaseContext::isActiveHero(const CGObjectInstance * obj) const
 	if(obj->ID == Obj::HERO)
 	if(obj->ID == Obj::HERO)
 	{
 	{
 		assert(dynamic_cast<const CGHeroInstance *>(obj) != nullptr);
 		assert(dynamic_cast<const CGHeroInstance *>(obj) != nullptr);
-		if(adventureInt->curHero() != nullptr)
+		if(LOCPLINT->localState->getCurrentHero() != nullptr)
 		{
 		{
-			if(obj->id == adventureInt->curHero()->id)
+			if(obj->id == LOCPLINT->localState->getCurrentHero()->id)
 				return true;
 				return true;
 		}
 		}
 	}
 	}
@@ -206,15 +206,15 @@ MapRendererAdventureContext::MapRendererAdventureContext(const MapRendererContex
 
 
 const CGPath * MapRendererAdventureContext::currentPath() const
 const CGPath * MapRendererAdventureContext::currentPath() const
 {
 {
-	const auto * hero = adventureInt->curHero();
+	const auto * hero = LOCPLINT->localState->getCurrentHero();
 
 
 	if(!hero)
 	if(!hero)
 		return nullptr;
 		return nullptr;
 
 
-	if(!LOCPLINT->paths.hasPath(hero))
+	if(!LOCPLINT->localState->hasPath(hero))
 		return nullptr;
 		return nullptr;
 
 
-	return &LOCPLINT->paths.getPath(hero);
+	return &LOCPLINT->localState->getPath(hero);
 }
 }
 
 
 size_t MapRendererAdventureContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const
 size_t MapRendererAdventureContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const

+ 1 - 1
client/mapView/MapRendererContextState.cpp

@@ -17,7 +17,7 @@
 #include "../../CCallback.h"
 #include "../../CCallback.h"
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 
 
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapping/CMap.h"
 #include "../../lib/mapping/CMap.h"

+ 1 - 1
client/mapView/MapView.cpp

@@ -19,7 +19,7 @@
 
 
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../render/CAnimation.h"
 #include "../render/CAnimation.h"
 #include "../render/Canvas.h"
 #include "../render/Canvas.h"

+ 1 - 1
client/mapView/MapViewActions.cpp

@@ -15,7 +15,7 @@
 #include "MapViewModel.h"
 #include "MapViewModel.h"
 
 
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CursorHandler.h"
 #include "../gui/CursorHandler.h"
 
 

+ 1 - 1
client/mapView/MapViewController.cpp

@@ -17,7 +17,7 @@
 #include "MapViewModel.h"
 #include "MapViewModel.h"
 
 
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CConfigHandler.h"

+ 8 - 7
client/windows/CCastleInterface.cpp

@@ -19,6 +19,7 @@
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/CComponent.h"
 #include "../widgets/CComponent.h"
@@ -27,7 +28,7 @@
 #include "../renderSDL/SDL_Extensions.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../render/IImage.h"
 #include "../render/IImage.h"
 #include "../render/ColorFilter.h"
 #include "../render/ColorFilter.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 #include "../adventureMap/CList.h"
 #include "../adventureMap/CList.h"
 #include "../adventureMap/CResDataBar.h"
 #include "../adventureMap/CResDataBar.h"
 
 
@@ -495,7 +496,7 @@ void HeroSlots::splitClicked()
 {
 {
 	if(!!town->visitingHero && town->garrisonHero && (visitingHero->isSelected() || garrisonedHero->isSelected()))
 	if(!!town->visitingHero && town->garrisonHero && (visitingHero->isSelected() || garrisonedHero->isSelected()))
 	{
 	{
-		LOCPLINT->heroExchangeStarted(town->visitingHero->id, town->garrisonHero->id, QueryID(-1));
+		LOCPLINT->showHeroExchange(town->visitingHero->id, town->garrisonHero->id);
 	}
 	}
 }
 }
 
 
@@ -1229,9 +1230,9 @@ void CCastleInterface::close()
 	if(town->tempOwner == LOCPLINT->playerID) //we may have opened window for an allied town
 	if(town->tempOwner == LOCPLINT->playerID) //we may have opened window for an allied town
 	{
 	{
 		if(town->visitingHero && town->visitingHero->tempOwner == LOCPLINT->playerID)
 		if(town->visitingHero && town->visitingHero->tempOwner == LOCPLINT->playerID)
-			adventureInt->select(town->visitingHero);
+			LOCPLINT->localState->setSelection(town->visitingHero);
 		else
 		else
-			adventureInt->select(town);
+			LOCPLINT->localState->setSelection(town);
 	}
 	}
 	CWindowObject::close();
 	CWindowObject::close();
 }
 }
@@ -1239,15 +1240,15 @@ void CCastleInterface::close()
 void CCastleInterface::castleTeleport(int where)
 void CCastleInterface::castleTeleport(int where)
 {
 {
 	const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(where));
 	const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(where));
-	adventureInt->select(town->visitingHero);//according to assert(ho == adventureInt->selection) in the eraseCurrentPathOf
+	LOCPLINT->localState->setSelection(town->visitingHero);//according to assert(ho == adventureInt->selection) in the eraseCurrentPathOf
 	LOCPLINT->cb->teleportHero(town->visitingHero, dest);
 	LOCPLINT->cb->teleportHero(town->visitingHero, dest);
-	LOCPLINT->paths.erasePath(town->visitingHero);
+	LOCPLINT->localState->erasePath(town->visitingHero);
 }
 }
 
 
 void CCastleInterface::townChange()
 void CCastleInterface::townChange()
 {
 {
 	//TODO: do not recreate window
 	//TODO: do not recreate window
-	const CGTownInstance * dest = LOCPLINT->towns[townlist->getSelectedIndex()];
+	const CGTownInstance * dest = LOCPLINT->localState->getOwnedTown(townlist->getSelectedIndex());
 	const CGTownInstance * town = this->town;// "this" is going to be deleted
 	const CGTownInstance * town = this->town;// "this" is going to be deleted
 	if ( dest == town )
 	if ( dest == town )
 		return;
 		return;

+ 1 - 1
client/windows/CQuestLog.cpp

@@ -15,7 +15,7 @@
 
 
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../widgets/CComponent.h"
 #include "../widgets/CComponent.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/Buttons.h"
 #include "../adventureMap/CMinimap.h"
 #include "../adventureMap/CMinimap.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../renderSDL/SDL_Extensions.h"

+ 8 - 7
client/windows/CSpellWindow.cpp

@@ -18,6 +18,7 @@
 
 
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../CVideoHandler.h"
 #include "../CVideoHandler.h"
 
 
 #include "../battle/BattleInterface.h"
 #include "../battle/BattleInterface.h"
@@ -25,7 +26,7 @@
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/CComponent.h"
 #include "../widgets/CComponent.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/TextControls.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 #include "../render/CAnimation.h"
 #include "../render/CAnimation.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../renderSDL/SDL_Extensions.h"
 
 
@@ -221,9 +222,9 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m
 		}
 		}
 	}
 	}
 
 
-	selectedTab = battleSpellsOnly ? myInt->spellbookSettings.spellbookLastTabBattle : myInt->spellbookSettings.spellbookLastTabAdvmap;
+	selectedTab = battleSpellsOnly ? myInt->localState->spellbookSettings.spellbookLastTabBattle : myInt->localState->spellbookSettings.spellbookLastTabAdvmap;
 	schoolTab->setFrame(selectedTab, 0);
 	schoolTab->setFrame(selectedTab, 0);
-	int cp = battleSpellsOnly ? myInt->spellbookSettings.spellbookLastPageBattle : myInt->spellbookSettings.spellbokLastPageAdvmap;
+	int cp = battleSpellsOnly ? myInt->localState->spellbookSettings.spellbookLastPageBattle : myInt->localState->spellbookSettings.spellbokLastPageAdvmap;
 	// spellbook last page battle index is not reset after battle, so this needs to stay here
 	// spellbook last page battle index is not reset after battle, so this needs to stay here
 	vstd::abetween(cp, 0, std::max(0, pagesWithinCurrentTab() - 1));
 	vstd::abetween(cp, 0, std::max(0, pagesWithinCurrentTab() - 1));
 	setCurrentPage(cp);
 	setCurrentPage(cp);
@@ -237,8 +238,8 @@ CSpellWindow::~CSpellWindow()
 
 
 void CSpellWindow::fexitb()
 void CSpellWindow::fexitb()
 {
 {
-	(myInt->battleInt ? myInt->spellbookSettings.spellbookLastTabBattle : myInt->spellbookSettings.spellbookLastTabAdvmap) = selectedTab;
-	(myInt->battleInt ? myInt->spellbookSettings.spellbookLastPageBattle : myInt->spellbookSettings.spellbokLastPageAdvmap) = currentPage;
+	(myInt->battleInt ? myInt->localState->spellbookSettings.spellbookLastTabBattle : myInt->localState->spellbookSettings.spellbookLastTabAdvmap) = selectedTab;
+	(myInt->battleInt ? myInt->localState->spellbookSettings.spellbookLastPageBattle : myInt->localState->spellbookSettings.spellbokLastPageAdvmap) = currentPage;
 
 
 	close();
 	close();
 }
 }
@@ -556,8 +557,8 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 
 
 			auto guard = vstd::makeScopeGuard([this]()
 			auto guard = vstd::makeScopeGuard([this]()
 			{
 			{
-				owner->myInt->spellbookSettings.spellbookLastTabAdvmap = owner->selectedTab;
-				owner->myInt->spellbookSettings.spellbokLastPageAdvmap = owner->currentPage;
+				owner->myInt->localState->spellbookSettings.spellbookLastTabAdvmap = owner->selectedTab;
+				owner->myInt->localState->spellbookSettings.spellbokLastPageAdvmap = owner->currentPage;
 			});
 			});
 
 
 			if(mySpell->getTargetType() == spells::AimType::LOCATION)
 			if(mySpell->getTargetType() == spells::AimType::LOCATION)

+ 7 - 6
client/windows/InfoWindows.cpp

@@ -11,6 +11,7 @@
 #include "InfoWindows.h"
 #include "InfoWindows.h"
 
 
 #include "../CGameInfo.h"
 #include "../CGameInfo.h"
+#include "../PlayerLocalState.h"
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
 #include "../CMusicHandler.h"
 #include "../CMusicHandler.h"
 
 
@@ -21,7 +22,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../battle/BattleInterface.h"
 #include "../battle/BattleInterface.h"
 #include "../battle/BattleInterfaceClasses.h"
 #include "../battle/BattleInterfaceClasses.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CAdventureMapInterface.h"
 #include "../windows/CMessage.h"
 #include "../windows/CMessage.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../gui/CursorHandler.h"
 #include "../gui/CursorHandler.h"
@@ -332,8 +333,8 @@ void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p,
 	}
 	}
 	else
 	else
 	{
 	{
-		if(adventureInt->curHero())
-			CRClickPopup::createAndPush(obj->getHoverText(adventureInt->curHero()));
+		if(LOCPLINT->localState->getCurrentHero())
+			CRClickPopup::createAndPush(obj->getHoverText(LOCPLINT->localState->getCurrentHero()));
 		else
 		else
 			CRClickPopup::createAndPush(obj->getHoverText(LOCPLINT->playerID));
 			CRClickPopup::createAndPush(obj->getHoverText(LOCPLINT->playerID));
 	}
 	}
@@ -376,7 +377,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGTownInstance * town)
 	: CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
 	: CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
 {
 {
 	InfoAboutTown iah;
 	InfoAboutTown iah;
-	LOCPLINT->cb->getTownInfo(town, iah, adventureInt->curTown()); //todo: should this be nearest hero?
+	LOCPLINT->cb->getTownInfo(town, iah, LOCPLINT->localState->getCurrentTown()); //todo: should this be nearest hero?
 
 
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	tooltip = std::make_shared<CTownTooltip>(Point(9, 10), iah);
 	tooltip = std::make_shared<CTownTooltip>(Point(9, 10), iah);
@@ -386,7 +387,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero)
 	: CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "HEROQVBK", toScreen(position))
 	: CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "HEROQVBK", toScreen(position))
 {
 {
 	InfoAboutHero iah;
 	InfoAboutHero iah;
-	LOCPLINT->cb->getHeroInfo(hero, iah, adventureInt->curHero());//todo: should this be nearest hero?
+	LOCPLINT->cb->getHeroInfo(hero, iah, LOCPLINT->localState->getCurrentHero());//todo: should this be nearest hero?
 
 
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	tooltip = std::make_shared<CHeroTooltip>(Point(9, 10), iah);
 	tooltip = std::make_shared<CHeroTooltip>(Point(9, 10), iah);
@@ -405,7 +406,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr)
 std::shared_ptr<WindowBase> CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
 std::shared_ptr<WindowBase> CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
 {
 {
 	if(nullptr == specific)
 	if(nullptr == specific)
-		specific = adventureInt->curArmy();
+		specific = LOCPLINT->localState->getCurrentArmy();
 
 
 	if(nullptr == specific)
 	if(nullptr == specific)
 	{
 	{

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -935,7 +935,7 @@ bool CGameInfoCallback::isInTheMap(const int3 &pos) const
 	return gs->map->isInTheMap(pos);
 	return gs->map->isInTheMap(pos);
 }
 }
 
 
-void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula) const
+void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula) const
 {
 {
 	gs->getTilesInRange(tiles, pos, radious, getLocalPlayer(), -1, distanceFormula);
 	gs->getTilesInRange(tiles, pos, radious, getLocalPlayer(), -1, distanceFormula);
 }
 }

+ 2 - 3
lib/CGameInfoCallback.h

@@ -35,7 +35,6 @@ struct SThievesGuildInfo;
 class CMapHeader;
 class CMapHeader;
 struct TeamState;
 struct TeamState;
 struct QuestInfo;
 struct QuestInfo;
-struct ShashInt3;
 class CGameState;
 class CGameState;
 class PathfinderConfig;
 class PathfinderConfig;
 
 
@@ -99,7 +98,7 @@ public:
 //	const TerrainTile * getTile(int3 tile, bool verbose = true) const;
 //	const TerrainTile * getTile(int3 tile, bool verbose = true) const;
 //	std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
 //	std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
 //	bool isInTheMap(const int3 &pos) const;
 //	bool isInTheMap(const int3 &pos) const;
-//	void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
+//	void getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
 
 
 	//town
 	//town
 //	const CGTownInstance* getTown(ObjectInstanceID objid) const;
 //	const CGTownInstance* getTown(ObjectInstanceID objid) const;
@@ -194,7 +193,7 @@ public:
 	virtual const TerrainTile * getTile(int3 tile, bool verbose = true) const;
 	virtual const TerrainTile * getTile(int3 tile, bool verbose = true) const;
 	virtual std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
 	virtual std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
 	virtual bool isInTheMap(const int3 &pos) const;
 	virtual bool isInTheMap(const int3 &pos) const;
-	virtual void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
+	virtual void getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
 	virtual void calculatePaths(const std::shared_ptr<PathfinderConfig> & config);
 	virtual void calculatePaths(const std::shared_ptr<PathfinderConfig> & config);
 	virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out);
 	virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out);
 	virtual EDiggingStatus getTileDigStatus(int3 tile, bool verbose = true) const;
 	virtual EDiggingStatus getTileDigStatus(int3 tile, bool verbose = true) const;

+ 1 - 1
lib/CGameState.cpp

@@ -1641,7 +1641,7 @@ void CGameState::initFogOfWar()
 		{
 		{
 			if(!obj || !vstd::contains(elem.second.players, obj->tempOwner)) continue; //not a flagged object
 			if(!obj || !vstd::contains(elem.second.players, obj->tempOwner)) continue; //not a flagged object
 
 
-			std::unordered_set<int3, ShashInt3> tiles;
+			std::unordered_set<int3> tiles;
 			getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), obj->tempOwner, 1);
 			getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), obj->tempOwner, 1);
 			for(const int3 & tile : tiles)
 			for(const int3 & tile : tiles)
 			{
 			{

+ 1 - 1
lib/CPathfinder.h

@@ -548,7 +548,7 @@ public:
 		PATROL_LOCKED = 1,
 		PATROL_LOCKED = 1,
 		PATROL_RADIUS
 		PATROL_RADIUS
 	} patrolState;
 	} patrolState;
-	std::unordered_set<int3, ShashInt3> patrolTiles;
+	std::unordered_set<int3> patrolTiles;
 
 
 	int turn;
 	int turn;
 	PlayerColor owner;
 	PlayerColor owner;

+ 2 - 2
lib/IGameCallback.cpp

@@ -63,7 +63,7 @@ void CPrivilegedInfoCallback::getFreeTiles(std::vector<int3> & tiles) const
 	}
 	}
 }
 }
 
 
-void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3> & tiles,
+void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
 											  const int3 & pos,
 											  const int3 & pos,
 											  int radious,
 											  int radious,
 											  std::optional<PlayerColor> player,
 											  std::optional<PlayerColor> player,
@@ -100,7 +100,7 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
 	}
 	}
 }
 }
 
 
-void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3, ShashInt3> & tiles, std::optional<PlayerColor> Player, int level, MapTerrainFilterMode tileFilterMode) const
+void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std::optional<PlayerColor> Player, int level, MapTerrainFilterMode tileFilterMode) const
 {
 {
 	if(!!Player && *Player >= PlayerColor::PLAYER_LIMIT)
 	if(!!Player && *Player >= PlayerColor::PLAYER_LIMIT)
 	{
 	{

+ 3 - 4
lib/IGameCallback.h

@@ -26,7 +26,6 @@ struct ArtifactLocation;
 class CCreatureSet;
 class CCreatureSet;
 class CStackBasicDescriptor;
 class CStackBasicDescriptor;
 class CGCreature;
 class CGCreature;
-struct ShashInt3;
 
 
 namespace spells
 namespace spells
 {
 {
@@ -59,7 +58,7 @@ public:
 	void getFreeTiles(std::vector<int3> &tiles) const;
 	void getFreeTiles(std::vector<int3> &tiles) const;
 
 
 	//mode 1 - only unrevealed tiles; mode 0 - all, mode -1 -  only revealed
 	//mode 1 - only unrevealed tiles; mode 0 - all, mode -1 -  only revealed
-	void getTilesInRange(std::unordered_set<int3, ShashInt3> & tiles,
+	void getTilesInRange(std::unordered_set<int3> & tiles,
 						 const int3 & pos,
 						 const int3 & pos,
 						 int radious,
 						 int radious,
 						 std::optional<PlayerColor> player = std::optional<PlayerColor>(),
 						 std::optional<PlayerColor> player = std::optional<PlayerColor>(),
@@ -67,7 +66,7 @@ public:
 						 int3::EDistanceFormula formula = int3::DIST_2D) const;
 						 int3::EDistanceFormula formula = int3::DIST_2D) const;
 
 
 	//returns all tiles on given level (-1 - both levels, otherwise number of level)
 	//returns all tiles on given level (-1 - both levels, otherwise number of level)
-	void getAllTiles(std::unordered_set<int3, ShashInt3> &tiles, std::optional<PlayerColor> player = std::optional<PlayerColor>(),
+	void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player = std::optional<PlayerColor>(),
 					 int level = -1, MapTerrainFilterMode tileFilterMode = MapTerrainFilterMode::NONE) const;
 					 int level = -1, MapTerrainFilterMode tileFilterMode = MapTerrainFilterMode::NONE) const;
 
 
 	//gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
 	//gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
@@ -136,7 +135,7 @@ public:
 	virtual void sendAndApply(CPackForClient * pack) = 0;
 	virtual void sendAndApply(CPackForClient * pack) = 0;
 	virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
 	virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
 	virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) = 0;
 	virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) = 0;
-	virtual void changeFogOfWar(std::unordered_set<int3, ShashInt3> &tiles, PlayerColor player, bool hide) = 0;
+	virtual void changeFogOfWar(std::unordered_set<int3> &tiles, PlayerColor player, bool hide) = 0;
 	
 	
 	virtual void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) = 0;
 	virtual void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) = 0;
 };
 };

+ 2 - 2
lib/IGameEventsReceiver.h

@@ -117,8 +117,8 @@ public:
 	virtual void showThievesGuildWindow (const CGObjectInstance * obj){};
 	virtual void showThievesGuildWindow (const CGObjectInstance * obj){};
 	virtual void showQuestLog(){};
 	virtual void showQuestLog(){};
 	virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID){}; //called when a hero casts a spell
 	virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID){}; //called when a hero casts a spell
-	virtual void tileHidden(const std::unordered_set<int3, ShashInt3> &pos){};
-	virtual void tileRevealed(const std::unordered_set<int3, ShashInt3> &pos){};
+	virtual void tileHidden(const std::unordered_set<int3> &pos){};
+	virtual void tileRevealed(const std::unordered_set<int3> &pos){};
 	virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard
 	virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard
 	virtual void availableArtifactsChanged(const CGBlackMarket *bm = nullptr){}; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
 	virtual void availableArtifactsChanged(const CGBlackMarket *bm = nullptr){}; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
 	virtual void centerView (int3 pos, int focusTime){};
 	virtual void centerView (int3 pos, int focusTime){};

+ 2 - 2
lib/NetPacks.h

@@ -308,7 +308,7 @@ struct DLL_LINKAGE FoWChange : public CPackForClient
 {
 {
 	void applyGs(CGameState * gs);
 	void applyGs(CGameState * gs);
 
 
-	std::unordered_set<int3, struct ShashInt3 > tiles;
+	std::unordered_set<int3> tiles;
 	PlayerColor player;
 	PlayerColor player;
 	ui8 mode = 0; //mode==0 - hide, mode==1 - reveal
 	ui8 mode = 0; //mode==0 - hide, mode==1 - reveal
 	bool waitForDialogs = false;
 	bool waitForDialogs = false;
@@ -590,7 +590,7 @@ struct DLL_LINKAGE TryMoveHero : public CPackForClient
 	ui32 movePoints = 0;
 	ui32 movePoints = 0;
 	EResult result = FAILED; //uses EResult
 	EResult result = FAILED; //uses EResult
 	int3 start, end; //h3m format
 	int3 start, end; //h3m format
-	std::unordered_set<int3, ShashInt3> fowRevealed; //revealed tiles
+	std::unordered_set<int3> fowRevealed; //revealed tiles
 	std::optional<int3> attackedFrom; // Set when stepping into endangered tile.
 	std::optional<int3> attackedFrom; // Set when stepping into endangered tile.
 
 
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 	virtual void visitTyped(ICPackVisitor & visitor) override;

+ 2 - 2
lib/NetPacksLib.cpp

@@ -915,7 +915,7 @@ void FoWChange::applyGs(CGameState *gs)
 		(*fogOfWarMap)[t.z][t.x][t.y] = mode;
 		(*fogOfWarMap)[t.z][t.x][t.y] = mode;
 	if (mode == 0) //do not hide too much
 	if (mode == 0) //do not hide too much
 	{
 	{
-		std::unordered_set<int3, ShashInt3> tilesRevealed;
+		std::unordered_set<int3> tilesRevealed;
 		for (auto & elem : gs->map->objects)
 		for (auto & elem : gs->map->objects)
 		{
 		{
 			const CGObjectInstance *o = elem;
 			const CGObjectInstance *o = elem;
@@ -1918,7 +1918,7 @@ void AssembledArtifact::applyGs(CGameState *gs)
 	[[maybe_unused]] const CArtifactInstance *transformedArt = al.getArt();
 	[[maybe_unused]] const CArtifactInstance *transformedArt = al.getArt();
 	assert(transformedArt);
 	assert(transformedArt);
 	bool combineEquipped = !ArtifactUtils::isSlotBackpack(al.slot);
 	bool combineEquipped = !ArtifactUtils::isSlotBackpack(al.slot);
-	assert(vstd::contains_if(transformedArt->assemblyPossibilities(artSet, combineEquipped), [=](const CArtifact * art)->bool
+	assert(vstd::contains_if(ArtifactUtils::assemblyPossibilities(artSet, transformedArt->artType->getId(), combineEquipped), [=](const CArtifact * art)->bool
 		{
 		{
 			return art->getId() == builtArt->getId();
 			return art->getId() == builtArt->getId();
 		}));
 		}));

+ 12 - 12
lib/int3.h

@@ -182,18 +182,6 @@ public:
 	}
 	}
 };
 };
 
 
-//Why not normal function?
-struct ShashInt3
-{
-	size_t operator()(int3 const& pos) const
-	{
-		size_t ret = std::hash<int>()(pos.x);
-		vstd::hash_combine(ret, pos.y);
-		vstd::hash_combine(ret, pos.z);
-		return ret;
-	}
-};
-
 template<typename Container>
 template<typename Container>
 int3 findClosestTile (Container & container, int3 dest)
 int3 findClosestTile (Container & container, int3 dest)
 {
 {
@@ -215,3 +203,15 @@ int3 findClosestTile (Container & container, int3 dest)
 }
 }
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END
+
+
+template<>
+struct std::hash<VCMI_LIB_WRAP_NAMESPACE(int3)> {
+	size_t operator()(VCMI_LIB_WRAP_NAMESPACE(int3) const& pos) const
+	{
+		size_t ret = std::hash<int>()(pos.x);
+		VCMI_LIB_WRAP_NAMESPACE(vstd)::hash_combine(ret, pos.y);
+		VCMI_LIB_WRAP_NAMESPACE(vstd)::hash_combine(ret, pos.z);
+		return ret;
+	}
+};

+ 3 - 3
server/CGameHandler.cpp

@@ -7136,11 +7136,11 @@ void CGameHandler::removeAfterVisit(const CGObjectInstance *object)
 
 
 void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide)
 void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide)
 {
 {
-	std::unordered_set<int3, ShashInt3> tiles;
+	std::unordered_set<int3> tiles;
 	getTilesInRange(tiles, center, radius, player, hide? -1 : 1);
 	getTilesInRange(tiles, center, radius, player, hide? -1 : 1);
 	if (hide)
 	if (hide)
 	{
 	{
-		std::unordered_set<int3, ShashInt3> observedTiles; //do not hide tiles observed by heroes. May lead to disastrous AI problems
+		std::unordered_set<int3> observedTiles; //do not hide tiles observed by heroes. May lead to disastrous AI problems
 		auto p = getPlayerState(player);
 		auto p = getPlayerState(player);
 		for (auto h : p->heroes)
 		for (auto h : p->heroes)
 		{
 		{
@@ -7156,7 +7156,7 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
 	changeFogOfWar(tiles, player, hide);
 	changeFogOfWar(tiles, player, hide);
 }
 }
 
 
-void CGameHandler::changeFogOfWar(std::unordered_set<int3, ShashInt3> &tiles, PlayerColor player, bool hide)
+void CGameHandler::changeFogOfWar(std::unordered_set<int3> &tiles, PlayerColor player, bool hide)
 {
 {
 	FoWChange fow;
 	FoWChange fow;
 	fow.tiles = tiles;
 	fow.tiles = tiles;

+ 1 - 1
server/CGameHandler.h

@@ -200,7 +200,7 @@ public:
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override;
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override;
 
 
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override;
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override;
-	void changeFogOfWar(std::unordered_set<int3, ShashInt3> &tiles, PlayerColor player, bool hide) override;
+	void changeFogOfWar(std::unordered_set<int3> &tiles, PlayerColor player, bool hide) override;
 	
 	
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override;
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override;
 
 

Some files were not shown because too many files changed in this diff