Browse Source

* Started making support for garrison window. Leaving guardians in flagged mines.

Michał W. Urbańczyk 16 years ago
parent
commit
f9aebcc4bd

+ 5 - 0
AI/GeniusAI/CGeniusAI.cpp

@@ -64,6 +64,11 @@ void CGeniusAI::heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector
 	callback(rand() % skills.size());
 }
 
+void GeniusAI::CGeniusAI::showGarrisonDialog( const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd )
+{
+	onEnd();
+}
+
 void CGeniusAI::showBlockingDialog( const std::string &text, const std::vector<Component> &components, ui32 askID, bool selection, bool cancel )
 {
 	m_cb->selectionMade(cancel ? 0 : 1, askID);

+ 1 - 0
AI/GeniusAI/CGeniusAI.h

@@ -194,6 +194,7 @@ public:
 	virtual void tileRevealed(int3 pos){};
 	virtual void tileHidden(int3 pos){};
 	virtual void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);
+	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd);
 	// battle
 	virtual void actionFinished(const BattleAction *action);//occurs AFTER every action taken by any stack or by the hero
 	virtual void actionStarted(const BattleAction *action);//occurs BEFORE every action taken by any stack or by the hero

+ 12 - 8
CAdvmapInterface.cpp

@@ -1364,15 +1364,17 @@ void CAdvMapInt::fendTurn()
 
 void CAdvMapInt::activate()
 {
-	if(active++)
-	{
-		tlog1 << "Error: advmapint already active...\n";
-	}
 	if(subInt == heroWindow)
 	{
 		heroWindow->activate();
 		return;
 	}
+	if(active++)
+	{
+		tlog1 << "Error: advmapint already active...\n";
+		active--;
+		return;
+	}
 	LOCPLINT->curint = this;
 	LOCPLINT->statusbar = &statusbar;
 	kingOverview.activate();
@@ -1397,10 +1399,6 @@ void CAdvMapInt::activate()
 }
 void CAdvMapInt::deactivate()
 {
-	if(--active)
-	{
-		tlog1 << "Error: advmapint still active...\n";
-	}
 	if(subInt == heroWindow)
 	{
 		heroWindow->deactivate();
@@ -1410,6 +1408,12 @@ void CAdvMapInt::deactivate()
 	hide();
 
 	LOCPLINT->cingconsole->deactivate();
+
+	if(--active)
+	{
+		tlog1 << "Error: advmapint still active...\n";
+		deactivate();
+	}
 }
 void CAdvMapInt::show(SDL_Surface *to)
 {

+ 8 - 10
CCallback.cpp

@@ -324,16 +324,14 @@ int CCallback::getHeroSerial(const CGHeroInstance * hero) const
 const CCreatureSet* CCallback::getGarrison(const CGObjectInstance *obj) const
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	if(!obj)
+	const CArmedInstance *armi = dynamic_cast<const CArmedInstance*>(obj);
+	if(!armi)
 		return NULL;
-	if(obj->ID == HEROI_TYPE)
-		return &(dynamic_cast<const CGHeroInstance*>(obj))->army;
-	else if(obj->ID == TOWNI_TYPE)
-		return &(dynamic_cast<const CGTownInstance*>(obj)->army);
-	else return NULL;
+	else 
+		return &armi->army;
 }
 
-int CCallback::swapCreatures(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)
+int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
 {
 	if(s1->tempOwner != player   ||   s2->tempOwner != player)
 		return -1;
@@ -343,8 +341,8 @@ int CCallback::swapCreatures(const CGObjectInstance *s1, const CGObjectInstance
 	return 0;
 }
 
-int CCallback::mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)
-{	
+int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
+{
 	if ((s1->tempOwner!= player  ||  s2->tempOwner!=player))
 	{
 		return -1;
@@ -353,7 +351,7 @@ int CCallback::mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s
 	*cl->serv << &pack;
 	return 0;
 }
-int CCallback::splitStack(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2, int val)
+int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val)
 {
 	if (s1->tempOwner!= player  ||  s2->tempOwner!=player || (!val))
 	{

+ 6 - 6
CCallback.h

@@ -33,9 +33,9 @@ class ICallback
 public:
 	virtual bool moveHero(const CGHeroInstance *h, int3 dst) const =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
 	virtual void selectionMade(int selection, int asker) =0;
-	virtual int swapCreatures(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)=0;//swaps creatures between two posiibly different garrisons // TODO: AI-unsafe code - fix it!
-	virtual int mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)=0;//joins first stack tothe second (creatures must be same type)
-	virtual int splitStack(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2, int val)=0;//split creatures from the first stack
+	virtual int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)=0;//swaps creatures between two posiibly different garrisons // TODO: AI-unsafe code - fix it!
+	virtual int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)=0;//joins first stack tothe second (creatures must be same type)
+	virtual int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val)=0;//split creatures from the first stack
 	virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses diven hero; true - successfuly, false - not successfuly
 	virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes
 	virtual void recruitCreatures(const CGObjectInstance *obj, ui32 ID, ui32 amount)=0;
@@ -123,9 +123,9 @@ public:
 //commands
 	bool moveHero(const CGHeroInstance *h, int3 dst) const; //dst must be free, neighbouring tile (this function can move hero only by one tile)
 	void selectionMade(int selection, int asker);
-	int swapCreatures(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2);
-	int mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2); //first goes to the second
-	int splitStack(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2, int val);
+	int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2);
+	int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2); //first goes to the second
+	int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val);
 	bool dismissHero(const CGHeroInstance * hero);
 	bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2);
 	bool buildBuilding(const CGTownInstance *town, si32 buildingID);

+ 1 - 0
CGameInterface.h

@@ -72,6 +72,7 @@ public:
 	//virtual void showSelDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
 	//virtual void showYesNoDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
 	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, bool selection, bool cancel) = 0; //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.
+	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done
 	virtual void tileHidden(const std::set<int3> &pos){};
 	virtual void tileRevealed(const std::set<int3> &pos){};
 	virtual void yourTurn(){};

+ 1 - 1
CHeroWindow.cpp

@@ -232,7 +232,7 @@ void CHeroWindow::setHero(const CGHeroInstance *Hero)
 	portraitArea->text = hero->getBiography();
 
 	delete garInt;
-	/*gar4button->owner = */garInt = new CGarrisonInt(pos.x+80, pos.y+493, 8, 0, curBack, 13, 482, curHero);
+	/*gar4button->owner = */garInt = new CGarrisonInt(pos.x+80, pos.y+493, 8, 0, curBack, 15, 485, curHero);
 	garInt->update = false;
 	gar4button->callback =  boost::bind(&CGarrisonInt::splitClick,garInt);//actualization of callback function
 

+ 80 - 9
CPlayerInterface.cpp

@@ -2117,11 +2117,6 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 			hw->garInt->recreateSlots();
 			hw->garInt->show();
 		}
-		if(castleInt) //opened town window - redraw town garrison slots (change is within hero garr)
-		{
-			castleInt->garr->highlighted = NULL;
-			castleInt->garr->recreateSlots();
-		}
 
 	}
 	else if (obj->ID == TOWNI_TYPE) //town
@@ -2137,12 +2132,24 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 			SDL_FreeSurface(graphics->heroWins[tt->visitingHero->subID]);
 			graphics->heroWins[tt->visitingHero->subID] = infoWin(tt->visitingHero);
 		}
-		if(LOCPLINT->castleInt)
+
+	}
+
+	if(castleInt) //opened town window - redraw town garrison slots (change is within hero garr)
+	{
+		castleInt->garr->highlighted = NULL;
+		castleInt->garr->recreateSlots();
+	}
+	else if(curint && curint->subInt)
+	{
+		CGarrisonWindow *gw = dynamic_cast<CGarrisonWindow*>(curint->subInt);
+		if(gw)
 		{
-			LOCPLINT->castleInt->garr->highlighted = NULL;
-			LOCPLINT->castleInt->garr->recreateSlots();
+			gw->garr->recreateSlots();
+			gw->garr->show();
 		}
 	}
+
 	if(curint == adventureInt)
 		adventureInt->infoBar.draw();
 }
@@ -2613,6 +2620,16 @@ bool CPlayerInterface::shiftPressed() const
 	return SDL_GetKeyState(NULL)[SDLK_LSHIFT]  ||  SDL_GetKeyState(NULL)[SDLK_RSHIFT];
 }
 
+void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd )
+{
+	CGarrisonWindow *cgw = new CGarrisonWindow(up,down);
+	cgw->quit->callback += onEnd;
+	curint->deactivate();
+	cgw->activate();
+	objsToBlit += cgw;
+	LOCPLINT->curint->subInt = cgw;
+}
+
 CStatusBar::CStatusBar(int x, int y, std::string name, int maxw)
 {
 	bg=BitmapHandler::loadBitmap(name);
@@ -3791,7 +3808,7 @@ void CCreInfoWindow::activate()
 void CCreInfoWindow::close()
 {
 	deactivate();
-	if(dynamic_cast<CHeroWindow*>(LOCPLINT->curint->subInt))
+	if(LOCPLINT->curint->subInt)
 	{
 		if(type)
 			LOCPLINT->curint->subInt->activate();
@@ -4791,3 +4808,57 @@ CInGameConsole::CInGameConsole() : defaultTimeout(10000), maxDisplayedTexts(10),
 {
 }
 
+
+void CGarrisonWindow::close()
+{
+	LOCPLINT->curint->subInt = NULL;
+	deactivate();
+	LOCPLINT->curint->activate();
+	LOCPLINT->objsToBlit -= this;
+	delete this;
+}
+
+void CGarrisonWindow::activate()
+{
+	split->activate();
+	quit->activate();
+	garr->activate();
+}
+
+void CGarrisonWindow::deactivate()
+{
+	split->deactivate();
+	quit->deactivate();
+	garr->deactivate();
+}
+
+void CGarrisonWindow::show(SDL_Surface * to)
+{
+	blitAt(bg,pos);
+	split->show();
+	quit->show();
+	garr->show();
+}
+
+CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance *down )
+{
+	bg = BitmapHandler::loadBitmap("GARRISON.bmp");
+	SDL_SetColorKey(bg,SDL_SRCCOLORKEY,SDL_MapRGB(bg->format,0,255,255));
+	graphics->blueToPlayersAdv(bg,LOCPLINT->playerID);
+	pos.x = screen->w/2 - bg->w/2;
+	pos.y = screen->h/2 - bg->h/2;
+	pos.w = screen->w;
+	pos.h = screen->h;
+
+	garr = new CGarrisonInt(pos.x+92, pos.y+129, 4, 30, bg, 92, 126, up, down);
+	split = new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+88,pos.y+314,"IDV6432.DEF");
+	quit = new AdventureMapButton(CGI->generaltexth->tcommands[8],"",boost::bind(&CGarrisonWindow::close,this),pos.x+399,pos.y+314,"IOK6432.DEF",SDLK_RETURN);
+}
+
+CGarrisonWindow::~CGarrisonWindow()
+{
+	SDL_FreeSurface(bg);
+	delete split;
+	delete quit;
+	delete garr;
+}

+ 16 - 0
CPlayerInterface.h

@@ -548,6 +548,7 @@ public:
 	//void showSelDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID);
 	//void showYesNoDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID);
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, bool selection, bool cancel); //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 showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd);
 	void tileHidden(const std::set<int3> &pos);
 	void tileRevealed(const std::set<int3> &pos);
 	void yourTurn();
@@ -927,6 +928,21 @@ public:
 	CInGameConsole(); //c-tor
 };
 
+class CGarrisonWindow : public IShowActivable, public CIntObject
+{
+public:
+	CGarrisonInt *garr;
+	SDL_Surface *bg;
+	AdventureMapButton *split, *quit;
+
+	void close();
+	void activate();
+	void deactivate();
+	void show(SDL_Surface * to = NULL);
+	CGarrisonWindow(const CArmedInstance *up, const CGHeroInstance *down);
+	~CGarrisonWindow();
+};
+
 extern CPlayerInterface * LOCPLINT;
 
 

+ 1 - 0
client/Client.h

@@ -79,6 +79,7 @@ public:
 	void showInfoDialog(InfoWindow *iw){};
 	void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback){};
 	ui32 showBlockingDialog(BlockingDialog *iw){return 0;}; //synchronous version of above
+	void showGarrisonDialog(int upobj, int hid, const boost::function<void()> &cb){};
 	void giveResource(int player, int which, int val){};
 	void showCompInfo(ShowInInfobox * comp){};
 	void heroVisitCastle(int obj, int heroID){};

+ 12 - 0
client/NetPacksClient.cpp

@@ -291,6 +291,18 @@ void BlockingDialog::applyCl( CClient *cl )
 		tlog2 << "We received YesNoDialog for not our player...\n";
 }
 
+void GarrisonDialog::applyCl(CClient *cl)
+{
+	const CGHeroInstance *h = cl->getHero(hid);
+	const CArmedInstance *obj = static_cast<const CArmedInstance*>(cl->getObj(objid));
+
+	if(!vstd::contains(cl->playerint,h->getOwner()))
+		return;
+
+	boost::function<void()> callback = boost::bind(&CCallback::selectionMade,LOCPLINT->cb,0,id);
+	cl->playerint[h->getOwner()]->showGarrisonDialog(obj,h,callback);
+}
+
 void BattleStart::applyCl( CClient *cl )
 {
 	if(vstd::contains(cl->playerint,info->side1))

+ 1 - 1
hch/CHeroHandler.cpp

@@ -119,7 +119,7 @@ void CHeroHandler::loadObstacles()
 	inp.open("config" PATHSEPARATOR "obstacles.txt", std::ios_base::in|std::ios_base::binary);
 	if(!inp.is_open())
 	{
-		tlog1<<"missing file: config/heroes_sec_skills.txt"<<std::endl;
+		tlog1<<"missing file: config/obstacles.txt"<<std::endl;
 	}
 	else
 	{

+ 4 - 1
hch/CObjectHandler.cpp

@@ -1292,7 +1292,10 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const
 		return;
 
 	if(h->tempOwner == tempOwner) //we're visiting our mine
-		return; //TODO: leaving garrison
+	{
+		cb->showGarrisonDialog(id,h->id,0);
+		return; 
+	}
 
 	//TODO: check if mine is guarded
 	cb->setOwner(id,h->tempOwner); //not ours? flag it!

+ 1 - 0
hch/CObjectHandler.h

@@ -599,6 +599,7 @@ public:
 class DLL_EXPORT CGMine : public CArmedInstance
 {
 public: 
+	void offerLeavingGuards(const CGHeroInstance *h) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void newTurn() const;
 	void initObj();	

+ 14 - 2
int3.h

@@ -11,9 +11,9 @@ class CCreatureSet //seven combined creatures
 public:
 	std::map<si32,std::pair<ui32,si32> > slots; //slots[slot_id]=> pair(creature_id,creature_quantity)
 	bool formation; //false - wide, true - tight
-	si32 getSlotFor(ui32 creature, ui32 slotsAmount=7) //returns -1 if no slot available
+	si32 getSlotFor(ui32 creature, ui32 slotsAmount=7) const //returns -1 if no slot available
 	{	
-		for(std::map<si32,std::pair<ui32,si32> >::iterator i=slots.begin(); i!=slots.end(); ++i)
+		for(std::map<si32,std::pair<ui32,si32> >::const_iterator i=slots.begin(); i!=slots.end(); ++i)
 		{
 			if(i->second.first == creature)
 			{
@@ -37,6 +37,18 @@ public:
 	{
 		return slots.size()>0;
 	}
+	void sweep()
+	{
+		for(std::map<si32,std::pair<ui32,si32> >::iterator i=slots.begin(); i!=slots.end(); ++i)
+		{
+			if(!i->second.second)
+			{
+				slots.erase(i);
+				sweep();
+				break;
+			}
+		}
+	}
 };
 
 class int3

+ 2 - 1
lib/IGameCallback.h

@@ -52,7 +52,8 @@ public:
 	virtual void changeSecSkill(int ID, int which, int val, bool abs=false)=0; 
 	virtual void showInfoDialog(InfoWindow *iw)=0;
 	virtual void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback)=0;
-	virtual ui32 showBlockingDialog(BlockingDialog *iw) =0; //synchronous version of above
+	virtual ui32 showBlockingDialog(BlockingDialog *iw) =0; //synchronous version of above //TODO:
+	virtual void showGarrisonDialog(int upobj, int hid, const boost::function<void()> &cb) =0; //cb will be called when player closes garrison window
 	virtual void giveResource(int player, int which, int val)=0;
 	virtual void showCompInfo(ShowInInfobox * comp)=0;
 	virtual void heroVisitCastle(int obj, int heroID)=0;

+ 12 - 0
lib/NetPacks.h

@@ -595,6 +595,18 @@ struct BlockingDialog : public Query//2003
 	}
 };
 
+struct GarrisonDialog : public Query//2004
+{
+	GarrisonDialog(){type = 2004;}
+	void applyCl(CClient *cl);
+	si32 objid, hid;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & id & objid & hid;
+	}
+};
+
 struct BattleInfo;
 struct BattleStart : public CPackForClient//3000
 {

+ 1 - 0
lib/RegisterTypes.cpp

@@ -68,6 +68,7 @@ void registerTypes2(Serializer &s)
 	s.template registerType<SetHoverName>();
 	s.template registerType<HeroLevelUp>();
 	s.template registerType<BlockingDialog>();
+	s.template registerType<GarrisonDialog>();
 	s.template registerType<BattleStart>();
 	s.template registerType<BattleNextRound>();
 	s.template registerType<BattleSetActiveStack>();

+ 71 - 7
server/CGameHandler.cpp

@@ -38,13 +38,9 @@ extern bool end2;
 		bnr.round = gs->curB->round + 1;\
 		sendAndApply(&bnr);
 
-boost::mutex gsm;
-ui32 CGameHandler::QID = 1;
-
 CondSh<bool> battleMadeAction;
 CondSh<BattleResult *> battleResult(NULL);
 
-std::map<ui32, CFunctionList<void(ui32)> > callbacks; //question id => callback function - for selection dialogs
 
 class CBaseForGHApply
 {
@@ -581,6 +577,7 @@ void CGameHandler::moveStack(int stack, int dest)
 }
 CGameHandler::CGameHandler(void)
 {
+	QID = 1;
 	gs = NULL;
 	IObjectInterface::cb = this;
 	applier = new CGHApplier;
@@ -1466,6 +1463,12 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 	CCreatureSet temp1 = s1->army, temp2 = s2->army,
 		&S1 = temp1, &S2 = (s1!=s2)?(temp2):(temp1);
 
+	if(!isAllowedExchange(id1,id2))
+	{
+		complain("Cannot exchange stacks between these two objects!\n");
+		return;
+	}
+
 	if(what==1) //swap
 	{
 		std::swap(S1.slots[p1],S2.slots[p2]); //swap slots
@@ -1925,10 +1928,25 @@ void CGameHandler::hireHero( ui32 tid, ui8 hid )
 void CGameHandler::queryReply( ui32 qid, ui32 answer )
 {
 	gsm.lock();
-	CFunctionList<void(ui32)> callb = callbacks[qid];
-	callbacks.erase(qid);
+	if(vstd::contains(callbacks,qid))
+	{
+		CFunctionList<void(ui32)> callb = callbacks[qid];
+		callbacks.erase(qid);
+		if(callb)
+			callb(answer);
+	}
+	else if(vstd::contains(garrisonCallbacks,qid))
+	{
+		if(garrisonCallbacks[qid])
+			garrisonCallbacks[qid]();
+		garrisonCallbacks.erase(qid);
+		allowedExchanges.erase(qid);
+	}
+	else
+	{
+		tlog1 << "Unknown query reply...\n";
+	}
 	gsm.unlock();
-	callb(answer);
 }
 
 void CGameHandler::makeBattleAction( BattleAction &ba )
@@ -2398,4 +2416,50 @@ ui32 CGameHandler::getQueryResult( ui8 player, int queryID )
 {
 	//TODO: write
 	return 0;
+}
+
+void CGameHandler::showGarrisonDialog( int upobj, int hid, const boost::function<void()> &cb )
+{
+	ui8 player = getOwner(hid);
+	GarrisonDialog gd;
+	gd.hid = hid;
+	gd.objid = upobj;
+	gsm.lock();
+	gd.id = QID;
+	garrisonCallbacks[QID] = cb;
+	allowedExchanges[QID] = std::pair<si32,si32>(upobj,hid);
+	states.addQuery(player,QID);
+	QID++; 
+	sendAndApply(&gd);
+	gsm.unlock();
+}
+
+bool CGameHandler::isAllowedExchange( int id1, int id2 )
+{
+	if(id1 == id2)
+		return true;
+
+	{
+		boost::unique_lock<boost::mutex> lock(gsm);
+		for(std::map<ui32, std::pair<si32,si32> >::const_iterator i = allowedExchanges.begin(); i!=allowedExchanges.end(); i++)
+			if(id1 == i->second.first && id2 == i->second.second   ||   id2 == i->second.first && id1 == i->second.second)
+				return true;
+	}
+
+	const CGObjectInstance *o1 = getObj(id1), *o2 = getObj(id2);
+
+	if(o1->ID == TOWNI_TYPE)
+	{
+		const CGTownInstance *t = static_cast<const CGTownInstance*>(o1);
+		if(t->visitingHero == o2  ||  t->garrisonHero == o2)
+			return true;
+	}
+	if(o2->ID == TOWNI_TYPE)
+	{
+		const CGTownInstance *t = static_cast<const CGTownInstance*>(o2);
+		if(t->visitingHero == o1  ||  t->garrisonHero == o1)
+			return true;
+	}
+
+	return false;
 }

+ 10 - 2
server/CGameHandler.h

@@ -58,12 +58,19 @@ public:
 class CGameHandler : public IGameCallback
 {
 public:
-	static ui32 QID;
 	CVCMIServer *s;
-	std::map<int,CConnection*> connections; //player color -> connection to clinet with interface of that player
+	std::map<int,CConnection*> connections; //player color -> connection to client with interface of that player
 	PlayerStatuses states; //player color -> player state
 	std::set<CConnection*> conns;
 
+	//queries stuff
+	boost::mutex gsm;
+	ui32 QID;
+	std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs
+	std::map<ui32, boost::function<void()> > garrisonCallbacks; //query id => callback - for garrison dialogs
+	std::map<ui32, std::pair<si32,si32> > allowedExchanges;
+
+	bool isAllowedExchange(int id1, int id2);
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 	void moveStack(int stack, int dest);
 	void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
@@ -95,6 +102,7 @@ public:
 	void showInfoDialog(InfoWindow *iw);
 	void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback);
 	ui32 showBlockingDialog(BlockingDialog *iw); //synchronous version of above
+	void showGarrisonDialog(int upobj, int hid, const boost::function<void()> &cb);
 	void giveResource(int player, int which, int val);
 	void showCompInfo(ShowInInfobox * comp);
 	void heroVisitCastle(int obj, int heroID);