瀏覽代碼

Fixed #216 and #907.
* CTRL+T will open marketplace window
* T in adventure map will switch to next town
* T in castle window will open a tavern window (if available)
* G will open thieves guild window if player owns at least one town with tavern
* various minor changes

Michał W. Urbańczyk 13 年之前
父節點
當前提交
3bf76cb719

+ 9 - 1
Global.h

@@ -65,6 +65,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/logic/tribool.hpp>
 #include <boost/program_options.hpp>
+#include <boost/range/algorithm.hpp>
 #include <boost/thread.hpp>
 #include <boost/unordered_set.hpp>
 
@@ -313,10 +314,15 @@ namespace vstd
 		return std::unique_ptr<T>(new T());
 	}
 	template<typename T, typename Arg1>
-	std::unique_ptr<T> make_unique(Arg1&& arg1)
+	std::unique_ptr<T> make_unique(Arg1 &&arg1)
 	{
 		return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1)));
 	}
+	template<typename T, typename Arg1, typename Arg2>
+	std::unique_ptr<T> make_unique(Arg1 &&arg1, Arg2 &&arg2)
+	{
+		return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2)));
+	}
 }
 
 using std::shared_ptr;
@@ -326,6 +332,8 @@ using vstd::make_unique;
 
 using vstd::operator-=;
 
+namespace range = boost::range;
+
 // can be used for counting arrays
 template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];
 #define ARRAY_COUNT(arr)    (sizeof(_ArrayCountObj(arr)))

+ 34 - 12
client/CAdvmapInterface.cpp

@@ -1453,6 +1453,19 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 
 	switch(k)
 	{
+	case SDLK_g:
+		if(key.state != SDL_PRESSED || GH.topInt()->type & BLOCK_ADV_HOTKEYS)
+			return;
+
+		{
+			//find first town with tavern
+			auto itr = range::find_if(LOCPLINT->towns, boost::bind(&CGTownInstance::hasBuilt, _1, EBuilding::TAVERN));
+			if(itr != LOCPLINT->towns.end())
+				LOCPLINT->showThievesGuildWindow(*itr);
+			else
+				LOCPLINT->showInfoDialog("No available town with tavern!");
+		}
+		return;
 	case SDLK_i:
 		if(isActive())
 			CAdventureOptions::showScenarioInfo();
@@ -1503,23 +1516,31 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 	case SDLK_t:
 		{
 			//act on key down if marketplace windows is not already opened
-			if(key.state != SDL_PRESSED  || GH.topInt()->type & BLOCK_ADV_HOTKEYS) return;
+			if(key.state != SDL_PRESSED || GH.topInt()->type & BLOCK_ADV_HOTKEYS)
+				return;
 
-			//check if we have any marketplace
-			const CGTownInstance *townWithMarket = NULL;
-			BOOST_FOREACH(const CGTownInstance *t, LOCPLINT->cb->getTownsInfo())
+			if(LOCPLINT->ctrlPressed()) //CTRL + T => open marketplace
 			{
-				if(vstd::contains(t->builtBuildings, 14))
+				//check if we have any marketplace
+				const CGTownInstance *townWithMarket = NULL;
+				BOOST_FOREACH(const CGTownInstance *t, LOCPLINT->cb->getTownsInfo())
 				{
-					townWithMarket = t;
-					break;
+					if(vstd::contains(t->builtBuildings, 14))
+					{
+						townWithMarket = t;
+						break;
+					}
 				}
-			}
 
-			if(townWithMarket) //if any town has marketplace, open window
-				GH.pushInt(new CMarketplaceWindow(townWithMarket));
-			else //if not - complain
-				LOCPLINT->showInfoDialog("No available marketplace!", std::vector<CComponent*>(), soundBase::sound_todo);
+				if(townWithMarket) //if any town has marketplace, open window
+					GH.pushInt(new CMarketplaceWindow(townWithMarket));
+				else //if not - complain
+					LOCPLINT->showInfoDialog("No available marketplace!");
+			}
+			else if(isActive()) //no ctrl, advmapint is on the top => switch to town
+			{
+				townList.selectNext();
+			}
 			return;
 		}
 	default:
@@ -2123,6 +2144,7 @@ void CAdvMapInt::adjustActiveness(bool aiTurnStart)
 	if(wasActive) 
 		activate();
 }
+
 CAdventureOptions::CAdventureOptions()
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;

+ 4 - 0
client/CCastleInterface.cpp

@@ -1257,6 +1257,10 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 			LOCPLINT->cb->swapGarrisonHero(town);
 		}
 		break;
+	case SDLK_t:
+		if(town->hasBuilt(EBuilding::TAVERN))
+			LOCPLINT->showTavernWindow(town);
+		break;
 	default:
 		break;
 	}

+ 6 - 0
client/CPlayerInterface.cpp

@@ -1415,6 +1415,12 @@ void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, const C
 
 void CPlayerInterface::waitWhileDialog(bool unlockPim /*= true*/)
 {
+	if(GH.amIGuiThread())
+	{
+		tlog3 << "Cannot wait for dialogs in gui thread (deadlock risk)!\n";
+		return;
+	}
+
 	auto unlock = vstd::makeUnlockGuardIf(*pim, unlockPim);
 	boost::unique_lock<boost::mutex> un(showingDialog->mx);
 	while(showingDialog->data)

+ 12 - 6
client/GUIClasses.cpp

@@ -1332,8 +1332,7 @@ void CTownList::genList()
 
 void CTownList::select(int which)
 {
-	if (which>=LOCPLINT->towns.size())
-		return;
+	which %= LOCPLINT->towns.size();
 	selected = which;
 	fixPos();
 	if(!fun.empty())
@@ -1528,6 +1527,11 @@ int CTownList::size()
 	return LOCPLINT->towns.size();
 }
 
+void CTownList::selectNext()
+{
+	select(selected+1);
+}
+
 CCreaturePic::CCreaturePic(int x, int y, const CCreature *cre, bool Big, bool Animated)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
@@ -5726,7 +5730,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket
 	yellow->recActions =
 	red->recActions    = DISPOSE;
 
-	if ( market->o->ID == 104 ) // this is adventure map university
+	if ( market->o->ID == Obj::UNIVERSITY ) // this is adventure map university
 	{
 		SDL_Surface * titleImage = BitmapHandler::loadBitmap("UNIVBLDG.PCX");
 		blitAtLoc(titleImage, 232-titleImage->w/2, 76-titleImage->h/2, bg->bg);
@@ -6028,6 +6032,7 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner)
 	:owner(_owner)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	type |= BLOCK_ADV_HOTKEYS;
 
 	SThievesGuildInfo tgi; //info to be displayed
 	LOCPLINT->cb->getThievesGuildInfo(tgi, owner);
@@ -6048,9 +6053,10 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner)
 	resdatabar->pos.x += pos.x;
 	resdatabar->pos.y += pos.y;
 
-	static std::vector< std::list< ui8 > > SThievesGuildInfo::* fields[] = { &SThievesGuildInfo::numOfTowns, &SThievesGuildInfo::numOfHeroes, &SThievesGuildInfo::gold,
-		&SThievesGuildInfo::woodOre, &SThievesGuildInfo::mercSulfCrystGems, &SThievesGuildInfo::obelisks, &SThievesGuildInfo::artifacts, &SThievesGuildInfo::army,
-		&SThievesGuildInfo::income};
+	static std::vector< std::list< ui8 > > SThievesGuildInfo::* fields[] = 
+		{ &SThievesGuildInfo::numOfTowns, &SThievesGuildInfo::numOfHeroes, &SThievesGuildInfo::gold,
+		  &SThievesGuildInfo::woodOre, &SThievesGuildInfo::mercSulfCrystGems, &SThievesGuildInfo::obelisks, 
+		  &SThievesGuildInfo::artifacts, &SThievesGuildInfo::army, &SThievesGuildInfo::income };
 
 	//printing texts & descriptions to background
 

+ 1 - 0
client/GUIClasses.h

@@ -322,6 +322,7 @@ public:
 	~CTownList(); //d-tor
 	void genList();
 	void select(int which); //call-in
+	void selectNext(); //switches to the next town or the first one if none is selected
 	void mouseMoved (const SDL_MouseMotionEvent & sEvent);  //call-in
 	void clickLeft(tribool down, bool previousState);  //call-in
 	void clickRight(tribool down, bool previousState); //call-in

+ 8 - 0
client/UIFramework/CGuiHandler.cpp

@@ -12,6 +12,8 @@ extern SDL_Surface * screenBuf, * screen2, * screen;
 extern std::queue<SDL_Event*> events;
 extern boost::mutex eventsM;
 
+boost::thread_specific_ptr<bool> inGuiThread;
+
 SObjectConstruction::SObjectConstruction( CIntObject *obj )
 :myObj(obj)
 {
@@ -344,6 +346,8 @@ void CGuiHandler::fakeMouseMove()
 void CGuiHandler::run()
 {
 	setThreadName(-1, "CGuiHandler::run");
+	bool iAmGui = true;
+	inGuiThread.reset(&iAmGui);
 	try
 	{
 		if (settings["video"]["fullscreen"].Bool())
@@ -457,6 +461,10 @@ bool CGuiHandler::isArrowKey( SDLKey key )
 	return key >= SDLK_UP && key <= SDLK_LEFT;
 }
 
+bool CGuiHandler::amIGuiThread()
+{
+	return inGuiThread.get() && *inGuiThread;
+}
 
 CFramerateManager::CFramerateManager(int rate)
 {

+ 1 - 0
client/UIFramework/CGuiHandler.h

@@ -96,6 +96,7 @@ public:
 	static SDLKey numToDigit(SDLKey key);//converts numpad digit key to normal digit key
 	static bool isNumKey(SDLKey key, bool number = true); //checks if key is on numpad (numbers - check only for numpad digits)
 	static bool isArrowKey(SDLKey key); 
+	static bool amIGuiThread();
 };
 
 extern CGuiHandler GH; //global gui handler

+ 7 - 26
lib/Connection.cpp

@@ -248,7 +248,6 @@ void CConnection::sendPackToServer(const CPack &pack, ui8 player, ui32 requestID
 }
 
 CSaveFile::CSaveFile( const std::string &fname )
-	:sfile(NULL)
 {
 	registerTypes(*this);
 	openNextFile(fname);
@@ -256,7 +255,6 @@ CSaveFile::CSaveFile( const std::string &fname )
 
 CSaveFile::~CSaveFile()
 {
-	delete sfile;
 }
 
 int CSaveFile::write( const void * data, unsigned size )
@@ -265,17 +263,10 @@ int CSaveFile::write( const void * data, unsigned size )
 	return size;
 }
 
-void CSaveFile::close()
-{
-	delete sfile;
-	sfile = NULL;
-}
-
 void CSaveFile::openNextFile(const std::string &fname)
 {
 	fName = fname;
-	close();
-	sfile = new std::ofstream(fname.c_str(),std::ios::binary);
+	sfile = make_unique<std::ofstream>(fname.c_str(), std::ios::binary);
 	if(!(*sfile))
 	{
 		tlog1 << "Error: cannot open to write " << fname << std::endl;
@@ -291,14 +282,13 @@ void CSaveFile::openNextFile(const std::string &fname)
 void CSaveFile::reportState(CLogger &out)
 {
 	out << "CSaveFile" << std::endl;
-	if(sfile && *sfile)
+	if(sfile.get() && *sfile)
 	{
 		out << "\tOpened " << fName << "\n\tPosition: " << sfile->tellp() << std::endl;
 	}
 }
 
 CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/)
-:sfile(NULL)
 {
 	registerTypes(*this);
 	openNextFile(fname, minimalVersion);
@@ -306,7 +296,6 @@ CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/)
 
 CLoadFile::~CLoadFile()
 {
-	delete sfile;
 }
 
 int CLoadFile::read( const void * data, unsigned size )
@@ -315,20 +304,14 @@ int CLoadFile::read( const void * data, unsigned size )
 	return size;
 }
 
-void CLoadFile::close()
-{
-	delete sfile;
-	sfile = NULL;
-}
-
 void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
 {
 	fName = fname;
-	sfile = new std::ifstream(fname.c_str(),std::ios::binary);
+	sfile = make_unique<std::ifstream>(fname.c_str(),std::ios::binary);
 	if(!(*sfile))
 	{
 		tlog1 << "Error: cannot open to read " << fname << std::endl;
-		sfile = NULL;
+		sfile.release();
 	}
 	else
 	{
@@ -338,8 +321,7 @@ void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
 		if(std::memcmp(buffer,"VCMI",4))
 		{
 			tlog1 << "Error: not a VCMI file! ( " << fname << " )\n";
-			delete sfile;
-			sfile = NULL;
+			sfile.release();
 			return;
 		}
 
@@ -347,8 +329,7 @@ void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
 		if(myVersion < minimalVersion)
 		{
 			tlog1 << "Error: Old file format! (file " << fname << " )\n";
-			delete sfile;
-			sfile = NULL;
+			sfile.release();
 		}
 	}
 }
@@ -356,7 +337,7 @@ void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
 void CLoadFile::reportState(CLogger &out)
 {
 	out << "CLoadFile" << std::endl;
-	if(sfile && *sfile)
+	if(!!sfile && *sfile)
 	{
 		out << "\tOpened " << fName << "\n\tPosition: " << sfile->tellg() << std::endl;
 	}

+ 2 - 5
lib/Connection.h

@@ -845,13 +845,12 @@ class DLL_LINKAGE CSaveFile
 	}
 public:
 	std::string fName;
-	std::ofstream *sfile;
+	unique_ptr<std::ofstream> sfile;
 
 	CSaveFile(const std::string &fname);
 	~CSaveFile();
 	int write(const void * data, unsigned size);
 
-	void close();
 	void openNextFile(const std::string &fname);
 	void reportState(CLogger &out);
 };
@@ -866,13 +865,12 @@ class DLL_LINKAGE CLoadFile
 	}
 public:
 	std::string fName;
-	std::ifstream *sfile;
+	unique_ptr<std::ifstream> sfile;
 
 	CLoadFile(const std::string &fname, int minimalVersion = version);
 	~CLoadFile();
 	int read(const void * data, unsigned size);
 
-	void close();
 	void openNextFile(const std::string &fname, int minimalVersion);
 	void reportState(CLogger &out);
 };
@@ -898,7 +896,6 @@ public:
 	std::string name; //who uses this connection
 
 	int connectionID;
-	CConnection *c;
 	boost::thread *handler;
 
 	bool receivedStop, sendStop;

+ 1 - 0
lib/GameConstants.h

@@ -199,6 +199,7 @@ namespace Obj
 		STABLES = 94,
 		TRADING_POST = 99,
 		SUBTERRANEAN_GATE = 103,
+		UNIVERSITY = 104,
 		SCHOOL_OF_WAR = 107,
 		WHIRLPOOL = 111,
 		BORDER_GATE = 212,

+ 17 - 8
lib/VCMI_Lib.cpp

@@ -234,14 +234,18 @@ void LibClasses::makeNull()
 LibClasses::LibClasses()
 {
 	//load .lod archives
-	CStopWatch pomtime;
-	spriteh = new CLodHandler();
-	spriteh->init(GameConstants::DATA_DIR + "/Data/H3sprite.lod", GameConstants::DATA_DIR + "/Sprites");
-	bitmaph = new CLodHandler;
-	bitmaph->init(GameConstants::DATA_DIR + "/Data/H3bitmap.lod", GameConstants::DATA_DIR + "/Data");
-	bitmaph_ab = new CLodHandler();
-	bitmaph_ab->init(GameConstants::DATA_DIR + "/Data/H3ab_bmp.lod", GameConstants::DATA_DIR + "/Data");
-	tlog0<<"Loading .lod files: "<<pomtime.getDiff()<<std::endl;
+
+	if(!spriteh) //don't reload lods if we are starting a secoond game
+	{
+		CStopWatch pomtime;
+		spriteh = new CLodHandler();
+		spriteh->init(GameConstants::DATA_DIR + "/Data/H3sprite.lod", GameConstants::DATA_DIR + "/Sprites");
+		bitmaph = new CLodHandler;
+		bitmaph->init(GameConstants::DATA_DIR + "/Data/H3bitmap.lod", GameConstants::DATA_DIR + "/Data");
+		bitmaph_ab = new CLodHandler();
+		bitmaph_ab->init(GameConstants::DATA_DIR + "/Data/H3ab_bmp.lod", GameConstants::DATA_DIR + "/Data");
+		tlog0<<"Loading .lod files: "<<pomtime.getDiff()<<std::endl;
+	}
 
 	//init pointers to handlers
 	makeNull();
@@ -253,3 +257,8 @@ void LibClasses::callWhenDeserializing()
 	generaltexth->load();
 	arth->loadArtifacts(true);
 }
+
+LibClasses::~LibClasses()
+{
+	clear();
+}

+ 1 - 0
lib/VCMI_Lib.h

@@ -37,6 +37,7 @@ public:
 	CGeneralTextHandler * generaltexth;
 
 	LibClasses(); //c-tor, loads .lods and NULLs handlers
+	~LibClasses();
 	void init(); //uses standard config file
 	void clear(); //deletes all handlers and its data
 	void makeNull(); //sets all handler (except of lodhs) pointers to null