Kaynağa Gözat

Moved player-specific heroes & towns information to new class

Ivan Savenko 2 yıl önce
ebeveyn
işleme
f6d480cbb2

+ 2 - 0
client/CMakeLists.txt

@@ -110,6 +110,7 @@ set(client_SRCS
 	CMT.cpp
 	CMusicHandler.cpp
 	CPlayerInterface.cpp
+	PlayerLocalState.cpp
 	CServerHandler.cpp
 	CVideoHandler.cpp
 	Client.cpp
@@ -238,6 +239,7 @@ set(client_HEADERS
 	CMT.h
 	CMusicHandler.h
 	CPlayerInterface.h
+	PlayerLocalState.h
 	CServerHandler.h
 	CVideoHandler.h
 	Client.h

+ 31 - 133
client/CPlayerInterface.cpp

@@ -8,6 +8,7 @@
  *
  */
 #include "StdInc.h"
+#include "CPlayerInterface.h"
 
 #include <vcmi/Artifact.h>
 
@@ -25,12 +26,12 @@
 #include "gui/CursorHandler.h"
 #include "windows/CKingdomInterface.h"
 #include "CGameInfo.h"
+#include "PlayerLocalState.h"
 #include "CMT.h"
 #include "windows/CHeroWindow.h"
 #include "windows/CCreatureWindow.h"
 #include "windows/CQuestLog.h"
 #include "windows/CPuzzleWindow.h"
-#include "CPlayerInterface.h"
 #include "widgets/CComponent.h"
 #include "widgets/Buttons.h"
 #include "windows/CTradeWindow.h"
@@ -114,96 +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->onHeroChanged(h);
-}
-
-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):
-	paths(*this)
+	localState(std::make_unique<PlayerLocalState>(*this))
 {
 	logGlobal->trace("\tHuman player interface for player %s being constructed", Player.getStr());
 	destinationTeleport = ObjectInstanceID();
@@ -401,27 +314,27 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 	};
 	adventureInt->onMapTilesChanged(changedTiles);
 
-	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(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)
 					&& (nodesIt - 1)->coord == hero->convertToVisitablePos(details.end))
 				{
 					//path was between entrance and exit of teleport -> OK, erase node as usual
-					paths.removeLastNode(hero);
+					localState->removeLastNode(hero);
 				}
 				else
 				{
 					//teleport was not along current path, it'll now be invalid (hero is somewhere else)
-					paths.erasePath(hero);
+					localState->erasePath(hero);
 
 				}
 			}
@@ -430,12 +343,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 		if(hero->pos != details.end //hero didn't change tile but visit succeeded
 			|| 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
-				paths.removeLastNode(hero);
+				localState->removeLastNode(hero);
 		}
 	}
 
@@ -492,9 +405,9 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->getNameTranslated() % playerID);
-	wanderingHeroes -= hero;
+	localState->wanderingHeroes -= hero;
 	adventureInt->onHeroChanged(hero);
-	paths.erasePath(hero);
+	localState->erasePath(hero);
 }
 
 void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start)
@@ -510,7 +423,7 @@ void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectI
 void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	wanderingHeroes.push_back(hero);
+	localState->wanderingHeroes.push_back(hero);
 	adventureInt->onHeroChanged(hero);
 }
 void CPlayerInterface::openTownWindow(const CGTownInstance * town)
@@ -596,14 +509,14 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 
 	if (town->garrisonHero) //wandering hero moved to the garrison
 	{
-		if (town->garrisonHero->tempOwner == playerID && vstd::contains(wanderingHeroes,town->garrisonHero)) // our hero
-			wanderingHeroes -= town->garrisonHero;
+		if (town->garrisonHero->tempOwner == playerID && vstd::contains(localState->wanderingHeroes,town->garrisonHero)) // our hero
+			localState->wanderingHeroes -= town->garrisonHero;
 	}
 
 	if (town->visitingHero) //hero leaves garrison
 	{
-		if (town->visitingHero->tempOwner == playerID && !vstd::contains(wanderingHeroes,town->visitingHero)) // our hero
-			wanderingHeroes.push_back(town->visitingHero);
+		if (town->visitingHero->tempOwner == playerID && !vstd::contains(localState->wanderingHeroes,town->visitingHero)) // our hero
+			localState->wanderingHeroes.push_back(town->visitingHero);
 	}
 	adventureInt->onHeroChanged(nullptr);
 	adventureInt->onTownChanged(town);
@@ -1311,29 +1224,20 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
 	if ((bonus.type == Bonus::FLYING_MOVEMENT || bonus.type == Bonus::WATER_WALKING) && !gain)
 	{
 		//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 )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	serializeTempl(h,version);
+	h & localState;
 }
 
 void CPlayerInterface::loadGame( BinaryDeserializer & h, const int version )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	serializeTempl(h,version);
+	h & localState;
 	firstCall = -1;
 }
 
@@ -1362,7 +1266,7 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	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();
 		return;
@@ -1441,9 +1345,9 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 			auto town = static_cast<const CGTownInstance *>(obj);
 
 			if(obj->tempOwner == playerID)
-				towns.push_back(town);
+				localState->ownedTowns.push_back(town);
 			else
-				towns -= obj;
+				localState->ownedTowns -= obj;
 
 			adventureInt->onTownChanged(town);
 		}
@@ -1452,24 +1356,24 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 		std::unordered_set<int3> upos(pos.begin(), pos.end());
 		adventureInt->onMapTilesChanged(upos);
 
-		assert(cb->getTownsInfo().size() == towns.size());
+		assert(cb->getTownsInfo().size() == localState->ownedTowns.size());
 	}
 }
 
 void CPlayerInterface::initializeHeroTownList()
 {
-	if(!wanderingHeroes.size())
+	if(!localState->wanderingHeroes.size())
 	{
 		std::vector<const CGHeroInstance*> heroes = cb->getHeroesInfo();
 		for(auto & hero : heroes)
 		{
 			if(!hero->inTownGarrison)
-				wanderingHeroes.push_back(hero);
+				localState->wanderingHeroes.push_back(hero);
 		}
 	}
 
-	if(!towns.size())
-		towns = cb->getTownsInfo();
+	if(!localState->ownedTowns.size())
+		localState->ownedTowns = cb->getTownsInfo();
 
 	if(adventureInt)
 		adventureInt->onHeroChanged(nullptr);
@@ -1736,7 +1640,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
 		GH.popInts(1);
 
 	if(spellID == SpellID::FLY || spellID == SpellID::WATER_WALK)
-		paths.erasePath(caster);
+		localState->erasePath(caster);
 
 	const spells::Spell * spell = CGI->spells()->getByIndex(spellID);
 	auto castSoundPath = spell->getCastSound();
@@ -1972,12 +1876,6 @@ void CPlayerInterface::proposeLoadingGame()
 	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()
 {
 	if(duringMovement)

+ 2 - 56
client/CPlayerInterface.h

@@ -9,16 +9,10 @@
  */
 #pragma once
 
-
 #include "../lib/FunctionList.h"
 #include "../lib/CGameInterface.h"
-#include "../lib/NetPacksBase.h"
 #include "gui/CIntObject.h"
 
-#ifdef __GNUC__
-#define sprintf_s snprintf
-#endif
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 class Artifact;
@@ -51,43 +45,16 @@ class ClickableR;
 class Hoverable;
 class KeyInterested;
 class MotionInterested;
+class PlayerLocalState;
 class TimeInterested;
 class IShowable;
 
-struct SDL_Surface;
-union SDL_Event;
-
 namespace boost
 {
 	class 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
 class CPlayerInterface : public CGameInterface, public IUpdateable
 {
@@ -107,7 +74,7 @@ class CPlayerInterface : public CGameInterface, public IUpdateable
 public: // TODO: make private
 	std::shared_ptr<Environment> env;
 
-	HeroPathStorage paths;
+	std::unique_ptr<PlayerLocalState> localState;
 
 	//minor interfaces
 	CondSh<bool> *showingDialog; //indicates if dialog box is displayed
@@ -121,29 +88,10 @@ public: // TODO: make private
 
 	std::shared_ptr<CCallback> cb; //to communicate with engine
 
-	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
 	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.
 
-	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;
-
 public:
 	void update() override;
 	void initializeHeroTownList();
@@ -267,8 +215,6 @@ public:
 	~CPlayerInterface();
 
 private:
-	template <typename Handler> void serializeTempl(Handler &h, const int version);
-
 	struct IgnoreEvents
 	{
 		CPlayerInterface & owner;

+ 105 - 0
client/PlayerLocalState.cpp

@@ -0,0 +1,105 @@
+/*
+ * 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 "CPlayerInterface.h"
+#include "adventureMap/CAdventureMapInterface.h"
+
+PlayerLocalState::PlayerLocalState()
+	: owner(*LOCPLINT)
+{
+	// should never be called, method required for serializer methods template instantiations
+	throw std::runtime_error("Can not create PlayerLocalState without interface!");
+}
+
+PlayerLocalState::PlayerLocalState(CPlayerInterface & owner)
+	: owner(owner)
+{
+}
+
+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());
+}

+ 84 - 0
client/PlayerLocalState.h

@@ -0,0 +1,84 @@
+/*
+ * 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;
+struct CGPath;
+class int3;
+
+VCMI_LIB_NAMESPACE_END
+
+class CPlayerInterface;
+
+/// Class that contains potentially serializeable state of a local player
+class PlayerLocalState
+{
+	CPlayerInterface & owner;
+
+	std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in 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;
+
+	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
+	std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
+
+	PlayerLocalState();
+	explicit PlayerLocalState(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 )
+	{
+		//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;
+	}
+};

+ 34 - 33
client/adventureMap/CAdventureMapInterface.cpp

@@ -38,6 +38,7 @@
 #include "../widgets/Buttons.h"
 #include "../windows/settings/SettingsMainWindow.h"
 #include "../CMT.h"
+#include "../PlayerLocalState.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CConfigHandler.h"
@@ -309,10 +310,10 @@ void CAdventureMapInterface::fsleepWake()
 void CAdventureMapInterface::fmoveHero()
 {
 	const CGHeroInstance *h = getCurrentHero();
-	if (!h || !LOCPLINT->paths.hasPath(h) || CGI->mh->hasOngoingAnimations())
+	if (!h || !LOCPLINT->localState->hasPath(h) || CGI->mh->hasOngoingAnimations())
 		return;
 
-	LOCPLINT->moveHero(h, LOCPLINT->paths.getPath(h));
+	LOCPLINT->moveHero(h, LOCPLINT->localState->getPath(h));
 }
 
 void CAdventureMapInterface::fshowSpellbok()
@@ -338,10 +339,10 @@ void CAdventureMapInterface::fsystemOptions()
 void CAdventureMapInterface::fnextHero()
 {
 	auto hero = dynamic_cast<const CGHeroInstance*>(currentSelection);
-	int next = getNextHeroIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
+	int next = getNextHeroIndex(vstd::find_pos(LOCPLINT->localState->wanderingHeroes, hero));
 	if (next < 0)
 		return;
-	setSelection(LOCPLINT->wanderingHeroes[next], true);
+	setSelection(LOCPLINT->localState->wanderingHeroes[next], true);
 }
 
 void CAdventureMapInterface::fendTurn()
@@ -351,22 +352,22 @@ void CAdventureMapInterface::fendTurn()
 
 	if(settings["adventure"]["heroReminder"].Bool())
 	{
-		for(auto hero : LOCPLINT->wanderingHeroes)
+		for(auto hero : LOCPLINT->localState->wanderingHeroes)
 		{
 			if(!isHeroSleeping(hero) && hero->movement > 0)
 			{
 				// Only show hero reminder if conditions met:
 				// - There still movement points
 				// - Hero don't have a path or there not points for first step on path
-				LOCPLINT->paths.verifyPath(hero);
+				LOCPLINT->localState->verifyPath(hero);
 
-				if(!LOCPLINT->paths.hasPath(hero))
+				if(!LOCPLINT->localState->hasPath(hero))
 				{
 					LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], std::bind(&CAdventureMapInterface::endingTurn, this), nullptr );
 					return;
 				}
 
-				auto path = LOCPLINT->paths.getPath(hero);
+				auto path = LOCPLINT->localState->getPath(hero);
 				if (path.nodes.size() < 2 || path.nodes[path.nodes.size() - 2].turns)
 				{
 					LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], std::bind(&CAdventureMapInterface::endingTurn, this), nullptr );
@@ -396,7 +397,7 @@ void CAdventureMapInterface::updateSpellbook(const CGHeroInstance *h)
 
 int CAdventureMapInterface::getNextHeroIndex(int startIndex)
 {
-	if (LOCPLINT->wanderingHeroes.size() == 0)
+	if (LOCPLINT->localState->wanderingHeroes.size() == 0)
 		return -1;
 	if (startIndex < 0)
 		startIndex = 0;
@@ -404,12 +405,12 @@ int CAdventureMapInterface::getNextHeroIndex(int startIndex)
 	do
 	{
 		i++;
-		if (i >= LOCPLINT->wanderingHeroes.size())
+		if (i >= LOCPLINT->localState->wanderingHeroes.size())
 			i = 0;
 	}
-	while (((LOCPLINT->wanderingHeroes[i]->movement == 0) || isHeroSleeping(LOCPLINT->wanderingHeroes[i])) && (i != startIndex));
+	while (((LOCPLINT->localState->wanderingHeroes[i]->movement == 0) || isHeroSleeping(LOCPLINT->localState->wanderingHeroes[i])) && (i != startIndex));
 
-	if ((LOCPLINT->wanderingHeroes[i]->movement != 0) && !isHeroSleeping(LOCPLINT->wanderingHeroes[i]))
+	if ((LOCPLINT->localState->wanderingHeroes[i]->movement != 0) && !isHeroSleeping(LOCPLINT->localState->wanderingHeroes[i]))
 		return i;
 	else
 		return -1;
@@ -422,14 +423,14 @@ void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
 	if (h == getCurrentHero())
 		infoBar->showSelection();
 
-	int start = vstd::find_pos(LOCPLINT->wanderingHeroes, h);
+	int start = vstd::find_pos(LOCPLINT->localState->wanderingHeroes, h);
 	int next = getNextHeroIndex(start);
 	if (next < 0)
 	{
 		nextHero->block(true);
 		return;
 	}
-	const CGHeroInstance *nextH = LOCPLINT->wanderingHeroes[next];
+	const CGHeroInstance *nextH = LOCPLINT->localState->wanderingHeroes[next];
 	bool noActiveHeroes = (next == start) && ((nextH->movement == 0) || isHeroSleeping(nextH));
 	nextHero->block(noActiveHeroes);
 
@@ -439,7 +440,7 @@ void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
 		return;
 	}
 	//default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
-	bool hasPath = LOCPLINT->paths.hasPath(h);
+	bool hasPath = LOCPLINT->localState->hasPath(h);
 
 	moveHero->block(!(bool)hasPath || (h->movement == 0));
 }
@@ -538,7 +539,7 @@ bool CAdventureMapInterface::isHeroSleeping(const CGHeroInstance *hero)
 	if (!hero)
 		return false;
 
-	return vstd::contains(LOCPLINT->sleepingHeroes, hero);
+	return vstd::contains(LOCPLINT->localState->sleepingHeroes, hero);
 }
 
 void CAdventureMapInterface::onHeroWokeUp(const CGHeroInstance * hero)
@@ -556,9 +557,9 @@ void CAdventureMapInterface::onHeroWokeUp(const CGHeroInstance * hero)
 void CAdventureMapInterface::setHeroSleeping(const CGHeroInstance *hero, bool sleep)
 {
 	if (sleep)
-		LOCPLINT->sleepingHeroes.push_back(hero); //FIXME: should we check for existence?
+		LOCPLINT->localState->sleepingHeroes.push_back(hero); //FIXME: should we check for existence?
 	else
-		LOCPLINT->sleepingHeroes -= hero;
+		LOCPLINT->localState->sleepingHeroes -= hero;
 
 	onHeroChanged(hero);
 }
@@ -654,7 +655,7 @@ void CAdventureMapInterface::handleMapScrollingUpdate()
 
 void CAdventureMapInterface::selectionChanged()
 {
-	const CGTownInstance *to = LOCPLINT->towns[townList->getSelectedIndex()];
+	const CGTownInstance *to = LOCPLINT->localState->ownedTowns[townList->getSelectedIndex()];
 	if (currentSelection != to)
 		setSelection(to);
 }
@@ -685,12 +686,12 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key)
 
 		{
 			//find first town with tavern
-			auto itr = range::find_if(LOCPLINT->towns, [](const CGTownInstance * town)
+			auto itr = range::find_if(LOCPLINT->localState->ownedTowns, [](const CGTownInstance * town)
 			{
 				return town->hasBuilt(BuildingID::TAVERN);
 			});
 
-			if(itr != LOCPLINT->towns.end())
+			if(itr != LOCPLINT->localState->ownedTowns.end())
 				LOCPLINT->showThievesGuildWindow(*itr);
 			else
 				LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern"));
@@ -819,10 +820,10 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key)
 			if (!CGI->mh->isInMap((dst)))
 				return;
 
-			if ( !LOCPLINT->paths.setPath(h, dst))
+			if ( !LOCPLINT->localState->setPath(h, dst))
 				return;
 
-			const CGPath & path = LOCPLINT->paths.getPath(h);
+			const CGPath & path = LOCPLINT->localState->getPath(h);
 
 			if (path.nodes.size() > 2)
 				onHeroChanged(h);
@@ -885,7 +886,7 @@ void CAdventureMapInterface::setSelection(const CArmedInstance *sel, bool center
 		heroList->select(hero);
 		townList->select(nullptr);
 
-		LOCPLINT->paths.verifyPath(hero);
+		LOCPLINT->localState->verifyPath(hero);
 
 		updateSleepWake(hero);
 		onHeroChanged(hero);
@@ -978,9 +979,9 @@ void CAdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
 	const CGHeroInstance * heroToSelect = nullptr;
 
 	// find first non-sleeping hero
-	for (auto hero : LOCPLINT->wanderingHeroes)
+	for (auto hero : LOCPLINT->localState->wanderingHeroes)
 	{
-		if (boost::range::find(LOCPLINT->sleepingHeroes, hero) == LOCPLINT->sleepingHeroes.end())
+		if (boost::range::find(LOCPLINT->localState->sleepingHeroes, hero) == LOCPLINT->localState->sleepingHeroes.end())
 		{
 			heroToSelect = hero;
 			break;
@@ -994,10 +995,10 @@ void CAdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
 	{
 		setSelection(heroToSelect, centerView);
 	}
-	else if (LOCPLINT->towns.size())
-		setSelection(LOCPLINT->towns.front(), centerView);
+	else if (LOCPLINT->localState->ownedTowns.size())
+		setSelection(LOCPLINT->localState->ownedTowns.front(), centerView);
 	else
-		setSelection(LOCPLINT->wanderingHeroes.front());
+		setSelection(LOCPLINT->localState->wanderingHeroes.front());
 
 	//show new day animation and sound on infobar
 	infoBar->showDate();
@@ -1102,16 +1103,16 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos)
 		}
 		else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
 		{
-			if(LOCPLINT->paths.hasPath(currentHero) &&
-			   LOCPLINT->paths.getPath(currentHero).endPos() == mapPos)//we'll be moving
+			if(LOCPLINT->localState->hasPath(currentHero) &&
+			   LOCPLINT->localState->getPath(currentHero).endPos() == mapPos)//we'll be moving
 			{
 				if(!CGI->mh->hasOngoingAnimations())
-					LOCPLINT->moveHero(currentHero, LOCPLINT->paths.getPath(currentHero));
+					LOCPLINT->moveHero(currentHero, LOCPLINT->localState->getPath(currentHero));
 				return;
 			}
 			else //remove old path and find a new one if we clicked on accessible tile
 			{
-				LOCPLINT->paths.setPath(currentHero, mapPos);
+				LOCPLINT->localState->setPath(currentHero, mapPos);
 				onHeroChanged(currentHero);
 			}
 		}

+ 2 - 1
client/adventureMap/CInfoBar.cpp

@@ -22,6 +22,7 @@
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../gui/CGuiHandler.h"
 
 #include "../../CCallback.h"
@@ -114,7 +115,7 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo()
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	//get amount of halls of each level
 	std::vector<int> halls(4, 0);
-	for(auto town : LOCPLINT->towns)
+	for(auto town : LOCPLINT->localState->ownedTowns)
 	{
 		int hallLevel = town->hallLevel();
 		//negative value means no village hall, unlikely but possible

+ 12 - 11
client/adventureMap/CList.cpp

@@ -18,6 +18,7 @@
 #include "../windows/InfoWindows.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../gui/CGuiHandler.h"
 
 #include "../../lib/CGeneralTextHandler.h"
@@ -224,19 +225,19 @@ std::string CHeroList::CHeroItem::getHoverText()
 
 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->wanderingHeroes.size() > index)
+		return std::make_shared<CHeroItem>(this, LOCPLINT->localState->wanderingHeroes[index]);
 	return std::make_shared<CEmptyHeroItem>();
 }
 
 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->wanderingHeroes.size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
 {
 }
 
 void CHeroList::select(const CGHeroInstance * hero)
 {
-	selectIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
+	selectIndex(vstd::find_pos(LOCPLINT->localState->wanderingHeroes, hero));
 }
 
 void CHeroList::update(const CGHeroInstance * hero)
@@ -245,7 +246,7 @@ void CHeroList::update(const CGHeroInstance * hero)
 	for(auto & elem : listBox->getItems())
 	{
 		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->wanderingHeroes, hero))
 		{
 			item->update();
 			return;
@@ -253,7 +254,7 @@ void CHeroList::update(const CGHeroInstance * hero)
 	}
 	//simplest solution for now: reset list and restore selection
 
-	listBox->resize(LOCPLINT->wanderingHeroes.size());
+	listBox->resize(LOCPLINT->localState->wanderingHeroes.size());
 	if (adventureInt->getCurrentHero())
 		select(adventureInt->getCurrentHero());
 
@@ -262,8 +263,8 @@ void CHeroList::update(const CGHeroInstance * hero)
 
 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->ownedTowns.size() > index)
+		return std::make_shared<CTownItem>(this, LOCPLINT->localState->ownedTowns[index]);
 	return std::make_shared<CAnimImage>("ITPA", 0);
 }
 
@@ -312,20 +313,20 @@ std::string CTownList::CTownItem::getHoverText()
 }
 
 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->ownedTowns.size(),  306, 307, std::bind(&CTownList::createTownItem, this, _1))
 {
 }
 
 void CTownList::select(const CGTownInstance * town)
 {
-	selectIndex(vstd::find_pos(LOCPLINT->towns, town));
+	selectIndex(vstd::find_pos(LOCPLINT->localState->ownedTowns, town));
 }
 
 void CTownList::update(const CGTownInstance *)
 {
 	//simplest solution for now: reset list and restore selection
 
-	listBox->resize(LOCPLINT->towns.size());
+	listBox->resize(LOCPLINT->localState->ownedTowns.size());
 	if (adventureInt->getCurrentTown())
 		select(adventureInt->getCurrentTown());
 

+ 3 - 2
client/mapView/MapRendererContext.cpp

@@ -17,6 +17,7 @@
 #include "../../CCallback.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../adventureMap/CAdventureMapInterface.h"
 
 #include "../../lib/CPathfinder.h"
@@ -211,10 +212,10 @@ const CGPath * MapRendererAdventureContext::currentPath() const
 	if(!hero)
 		return nullptr;
 
-	if(!LOCPLINT->paths.hasPath(hero))
+	if(!LOCPLINT->localState->hasPath(hero))
 		return nullptr;
 
-	return &LOCPLINT->paths.getPath(hero);
+	return &LOCPLINT->localState->getPath(hero);
 }
 
 size_t MapRendererAdventureContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const

+ 3 - 2
client/windows/CCastleInterface.cpp

@@ -19,6 +19,7 @@
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../gui/CGuiHandler.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/CComponent.h"
@@ -1241,13 +1242,13 @@ void CCastleInterface::castleTeleport(int where)
 	const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(where));
 	adventureInt->setSelection(town->visitingHero);//according to assert(ho == adventureInt->selection) in the eraseCurrentPathOf
 	LOCPLINT->cb->teleportHero(town->visitingHero, dest);
-	LOCPLINT->paths.erasePath(town->visitingHero);
+	LOCPLINT->localState->erasePath(town->visitingHero);
 }
 
 void CCastleInterface::townChange()
 {
 	//TODO: do not recreate window
-	const CGTownInstance * dest = LOCPLINT->towns[townlist->getSelectedIndex()];
+	const CGTownInstance * dest = LOCPLINT->localState->ownedTowns[townlist->getSelectedIndex()];
 	const CGTownInstance * town = this->town;// "this" is going to be deleted
 	if ( dest == town )
 		return;

+ 7 - 6
client/windows/CSpellWindow.cpp

@@ -18,6 +18,7 @@
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../CVideoHandler.h"
 
 #include "../battle/BattleInterface.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);
-	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
 	vstd::abetween(cp, 0, std::max(0, pagesWithinCurrentTab() - 1));
 	setCurrentPage(cp);
@@ -237,8 +238,8 @@ CSpellWindow::~CSpellWindow()
 
 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();
 }
@@ -556,8 +557,8 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 
 			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)