Procházet zdrojové kódy

Big change: Introduced new mechanism to handle queries. It should not cause any visible changes ATM apart from fixing several long-standing bugs realted to handling post-visit/battle/levelup callback, including infamous creature bank issues: #955, #1053, #1063, #1191. Needs testing.

Minor changes:
* default log level set to trace
* LOG_TRACE raii guardian lifetime will last till the end of block
* compile fixes
* minor refactorings
Michał W. Urbańczyk před 12 roky
rodič
revize
e8354908c3

+ 2 - 19
AI/VCAI/VCAI.cpp

@@ -43,24 +43,6 @@ struct SetGlobalState
 	}
 	}
 };
 };
 
 
-template <typename Container>
-typename Container::value_type backOrNull(const Container &c) //returns last element of container or NULL if it is empty (to be used with containers of pointers)
-{
-	if(c.size())
-		return c.back();
-	else
-		return NULL;
-}
-
-template <typename Container>
-typename Container::value_type frontOrNull(const Container &c) //returns first element of container or NULL if it is empty (to be used with containers of pointers)
-{
-	if(c.size())
-		return c.front();
-	else
-		return NULL;
-}
-
 
 
 #define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai);
 #define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai);
 
 
@@ -793,7 +775,7 @@ void VCAI::heroManaPointsChanged(const CGHeroInstance * hero)
 
 
 void VCAI::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
 void VCAI::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
 {
 {
-	LOG_TRACE_PARAMS(logAi, "which '%', val '%'", which % val);
+	LOG_TRACE_PARAMS(logAi, "which '%d', val '%d'", which % val);
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;
 }
 }
 
 
@@ -2579,6 +2561,7 @@ AIStatus::~AIStatus()
 void AIStatus::setBattle(BattleState BS)
 void AIStatus::setBattle(BattleState BS)
 {
 {
 	boost::unique_lock<boost::mutex> lock(mx);
 	boost::unique_lock<boost::mutex> lock(mx);
+	LOG_TRACE_PARAMS(logAi, "battle state=%d", (int)BS);
 	battle = BS;
 	battle = BS;
 	cv.notify_all();
 	cv.notify_all();
 }
 }

+ 18 - 0
Global.h

@@ -568,6 +568,24 @@ namespace vstd
 	{
 	{
 		obj = (T)(((int)obj) + change);
 		obj = (T)(((int)obj) + change);
 	}
 	}
+
+	template <typename Container>
+	typename Container::value_type backOrNull(const Container &c) //returns last element of container or NULL if it is empty (to be used with containers of pointers)
+	{
+		if(c.size())
+			return c.back();
+		else
+			return NULL;
+	}
+
+	template <typename Container>
+	typename Container::value_type frontOrNull(const Container &c) //returns first element of container or NULL if it is empty (to be used with containers of pointers)
+	{
+		if(c.size())
+			return c.front();
+		else
+			return NULL;
+	}
 }
 }
 using vstd::operator-=;
 using vstd::operator-=;
 using vstd::make_unique;
 using vstd::make_unique;

+ 1 - 1
VCMI_global.props

@@ -17,4 +17,4 @@
     </ClCompile>
     </ClCompile>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup />
   <ItemGroup />
-</Project>
+</Project>

+ 3 - 1
client/CMT.cpp

@@ -176,7 +176,9 @@ static void prog_help(const po::options_description &opts)
 void OSX_checkForUpdates();
 void OSX_checkForUpdates();
 #endif
 #endif
 
 
-#if defined(__APPLE__)
+#ifdef _WIN32
+int wmain(int argc, wchar_t* argv[])
+#elif defined(__APPLE__)
 int SDL_main(int argc, char *argv[])
 int SDL_main(int argc, char *argv[])
 #else
 #else
 int main(int argc, char** argv)
 int main(int argc, char** argv)

+ 24 - 24
client/CPlayerInterface.cpp

@@ -230,10 +230,10 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	if(LOCPLINT != this)
 	if(LOCPLINT != this)
 		return;
 		return;
 
 
-	const CGHeroInstance * ho = cb->getHero(details.id); //object representing this hero
+	const CGHeroInstance * hero = cb->getHero(details.id); //object representing this hero
 	int3 hp = details.start;
 	int3 hp = details.start;
 
 
-	if(!ho)
+	if(!hero)
 	{
 	{
 		//AI hero left the visible area (we can't obtain info)
 		//AI hero left the visible area (we can't obtain info)
 		//TODO very evil workaround -> retreive pointer to hero so we could animate it
 		//TODO very evil workaround -> retreive pointer to hero so we could animate it
@@ -241,25 +241,25 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 		const TerrainTile2 &tile = CGI->mh->ttiles[hp.x-1][hp.y][hp.z];
 		const TerrainTile2 &tile = CGI->mh->ttiles[hp.x-1][hp.y][hp.z];
 		for(int i = 0; i < tile.objects.size(); i++)
 		for(int i = 0; i < tile.objects.size(); i++)
 			if(tile.objects[i].first->id == details.id)
 			if(tile.objects[i].first->id == details.id)
-				ho = dynamic_cast<const CGHeroInstance *>(tile.objects[i].first);
+				hero = dynamic_cast<const CGHeroInstance *>(tile.objects[i].first);
 
 
-		if(!ho) //still nothing...
+		if(!hero) //still nothing...
 			return;
 			return;
 	}
 	}
 
 
-	adventureInt->centerOn(ho); //actualizing screen pos
+	adventureInt->centerOn(hero); //actualizing screen pos
 	adventureInt->minimap.redraw();
 	adventureInt->minimap.redraw();
 	adventureInt->heroList.redraw();
 	adventureInt->heroList.redraw();
 
 
 	bool directlyAttackingCreature =
 	bool directlyAttackingCreature =
-		CGI->mh->map->isInTheMap(details.attackedFrom)
+		details.attackedFrom
 		&& adventureInt->terrain.currentPath					//in case if movement has been canceled in the meantime and path was already erased
 		&& adventureInt->terrain.currentPath					//in case if movement has been canceled in the meantime and path was already erased
 		&& adventureInt->terrain.currentPath->nodes.size() == 3;//FIXME should be 2 but works nevertheless...
 		&& adventureInt->terrain.currentPath->nodes.size() == 3;//FIXME should be 2 but works nevertheless...
 
 
-	if(makingTurn  &&  ho->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
 	{
 	{
 		//We may need to change music - select new track, music handler will change it if needed
 		//We may need to change music - select new track, music handler will change it if needed
-		CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(ho->visitablePos())->terType, true);
+		CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType, true);
 
 
 		if(details.result == TryMoveHero::TELEPORTATION)
 		if(details.result == TryMoveHero::TELEPORTATION)
 		{
 		{
@@ -272,42 +272,42 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 					&& (nodesIt-1)->coord == CGHeroInstance::convertPosition(details.end, false))
 					&& (nodesIt-1)->coord == CGHeroInstance::convertPosition(details.end, false))
 				{
 				{
 					//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
-					removeLastNodeFromPath(ho);
+					removeLastNodeFromPath(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)
-					eraseCurrentPathOf(ho);
+					eraseCurrentPathOf(hero);
 
 
 				}
 				}
 			}
 			}
-			adventureInt->heroList.update(ho);
+			adventureInt->heroList.update(hero);
 			return;	//teleport - no fancy moving animation
 			return;	//teleport - no fancy moving animation
 					//TODO: smooth disappear / appear effect
 					//TODO: smooth disappear / appear effect
 		}
 		}
 
 
-		if (ho->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.
 		{
 		{
-			eraseCurrentPathOf(ho, false);
+			eraseCurrentPathOf(hero, false);
 		}
 		}
-		else if(adventureInt->terrain.currentPath  &&  ho->pos == details.end) //&& hero is moving
+		else if(adventureInt->terrain.currentPath  &&  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
-				removeLastNodeFromPath(ho);
+				removeLastNodeFromPath(hero);
 		}
 		}
 	}
 	}
 
 
 	if (details.result != TryMoveHero::SUCCESS) //hero failed to move
 	if (details.result != TryMoveHero::SUCCESS) //hero failed to move
 	{
 	{
-		ho->isStanding = true;
+		hero->isStanding = true;
 		stillMoveHero.setn(STOP_MOVE);
 		stillMoveHero.setn(STOP_MOVE);
 		GH.totalRedraw();
 		GH.totalRedraw();
-		adventureInt->heroList.update(ho);
+		adventureInt->heroList.update(hero);
 		return;
 		return;
 	}
 	}
 
 
-	initMovement(details, ho, hp);
+	initMovement(details, hero, hp);
 
 
 	//first initializing done
 	//first initializing done
 	GH.mainFPSmng->framerateDelay(); // after first move
 	GH.mainFPSmng->framerateDelay(); // after first move
@@ -316,7 +316,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	//main moving
 	//main moving
 	for(int i=1; i<32; i+=2*speed)
 	for(int i=1; i<32; i+=2*speed)
 	{
 	{
-		movementPxStep(details, i, hp, ho);
+		movementPxStep(details, i, hp, hero);
 		adventureInt->updateScreen = true;
 		adventureInt->updateScreen = true;
 		adventureInt->show(screen);
 		adventureInt->show(screen);
 		CSDL_Ext::update(screen);
 		CSDL_Ext::update(screen);
@@ -325,12 +325,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	//main moving done
 	//main moving done
 
 
 	//finishing move
 	//finishing move
-	finishMovement(details, hp, ho);
-	ho->isStanding = true;
+	finishMovement(details, hp, hero);
+	hero->isStanding = true;
 
 
 	//move finished
 	//move finished
 	adventureInt->minimap.redraw();
 	adventureInt->minimap.redraw();
-	adventureInt->heroList.update(ho);
+	adventureInt->heroList.update(hero);
 
 
 	//check if user cancelled movement
 	//check if user cancelled movement
 	{
 	{
@@ -358,14 +358,14 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	// Hero attacked creature directly, set direction to face it.
 	// Hero attacked creature directly, set direction to face it.
 	if (directlyAttackingCreature) {
 	if (directlyAttackingCreature) {
 		// Get direction to attacker.
 		// Get direction to attacker.
-		int3 posOffset = details.attackedFrom - details.end + int3(2, 1, 0);
+		int3 posOffset = *details.attackedFrom - details.end + int3(2, 1, 0);
 		static const ui8 dirLookup[3][3] = {
 		static const ui8 dirLookup[3][3] = {
 			{ 1, 2, 3 },
 			{ 1, 2, 3 },
 			{ 8, 0, 4 },
 			{ 8, 0, 4 },
 			{ 7, 6, 5 }
 			{ 7, 6, 5 }
 		};
 		};
 		// FIXME: Avoid const_cast, make moveDir mutable in some other way?
 		// FIXME: Avoid const_cast, make moveDir mutable in some other way?
-		const_cast<CGHeroInstance *>(ho)->moveDir = dirLookup[posOffset.y][posOffset.x];
+		const_cast<CGHeroInstance *>(hero)->moveDir = dirLookup[posOffset.y][posOffset.x];
 	}
 	}
 }
 }
 void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 void CPlayerInterface::heroKilled(const CGHeroInstance* hero)

+ 7 - 6
client/Client.h

@@ -170,11 +170,12 @@ public:
 	void setHoverName(const CGObjectInstance * obj, MetaString * name) OVERRIDE {};
 	void setHoverName(const CGObjectInstance * obj, MetaString * name) OVERRIDE {};
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE {};
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE {};
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE {}; 
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE {}; 
-	void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback) OVERRIDE {};
-	ui32 showBlockingDialog(BlockingDialog *iw) OVERRIDE {return 0;}; //synchronous version of above
+
+	void showBlockingDialog(BlockingDialog *iw) OVERRIDE {}; 
 	void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE {};
 	void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE {};
 	void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE {};
 	void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE {};
 	void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE {};
 	void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE {};
+	virtual void giveResources(PlayerColor player, TResources resources) OVERRIDE {};
 
 
 	void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE {};
 	void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE {};
 	void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE {};
 	void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE {};
@@ -199,11 +200,11 @@ public:
 	void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE {};
 	void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE {};
 	//void giveHeroArtifact(int artid, int hid, int position){}; 
 	//void giveHeroArtifact(int artid, int hid, int position){}; 
 	//void giveNewArtifact(int hid, int position){};
 	//void giveNewArtifact(int hid, int position){};
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL) OVERRIDE {}; //use hero=NULL for no hero
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
+	void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = NULL) OVERRIDE {}; //use hero=NULL for no hero
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
 	void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE {};
 	void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE {};
-	bool moveHero(ObjectInstanceID hid, int3 dst, ui8 instant, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE {return false;};
+	bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE {return false;};
 	void giveHeroBonus(GiveBonus * bonus) OVERRIDE {};
 	void giveHeroBonus(GiveBonus * bonus) OVERRIDE {};
 	void setMovePoints(SetMovePoints * smp) OVERRIDE {};
 	void setMovePoints(SetMovePoints * smp) OVERRIDE {};
 	void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE {};
 	void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE {};

+ 4 - 5
client/NetPacksClient.cpp

@@ -545,11 +545,10 @@ void SetObjectProperty::applyCl( CClient *cl )
 
 
 void HeroLevelUp::applyCl( CClient *cl )
 void HeroLevelUp::applyCl( CClient *cl )
 {
 {
-	const CGHeroInstance *h = cl->getHero(heroid);
 	//INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroGotLevel, h, primskill, skills, id);
 	//INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroGotLevel, h, primskill, skills, id);
-	if(vstd::contains(cl->playerint,h->tempOwner))
+	if(vstd::contains(cl->playerint,hero->tempOwner))
 	{
 	{
-		cl->playerint[h->tempOwner]->heroGotLevel(h, primskill, skills, queryID);
+		cl->playerint[hero->tempOwner]->heroGotLevel(hero, primskill, skills, queryID);
 	}
 	}
 	//else
 	//else
 	//	cb->selectionMade(0, queryID);
 	//	cb->selectionMade(0, queryID);
@@ -557,9 +556,9 @@ void HeroLevelUp::applyCl( CClient *cl )
 
 
 void CommanderLevelUp::applyCl( CClient *cl )
 void CommanderLevelUp::applyCl( CClient *cl )
 {
 {
-	CCommanderInstance * commander = GS(cl)->getHero(heroid)->commander;
+	const CCommanderInstance * commander = hero->commander;
 	assert (commander);
 	assert (commander);
-	PlayerColor player = commander->armyObj->tempOwner;
+	PlayerColor player = hero->tempOwner;
 	if (commander->armyObj && vstd::contains(cl->playerint, player)) //is it possible for Commander to exist beyond armed instance?
 	if (commander->armyObj && vstd::contains(cl->playerint, player)) //is it possible for Commander to exist beyond armed instance?
 	{
 	{
 		cl->playerint[player]->commanderGotLevel(commander, skills, queryID);
 		cl->playerint[player]->commanderGotLevel(commander, skills, queryID);

+ 1 - 1
config/schemas/settings.json

@@ -207,7 +207,7 @@
 				},
 				},
 				"loggers" : {
 				"loggers" : {
 					"type" : "array",
 					"type" : "array",
-					"default" : [ { "domain" : "global", "level" : "info" } ],
+					"default" : [ { "domain" : "global", "level" : "trace" } ],
 					"items" : {
 					"items" : {
 						"type" : "object",
 						"type" : "object",
 						"additionalProperties" : false,
 						"additionalProperties" : false,

+ 5 - 0
lib/CCreatureSet.cpp

@@ -741,6 +741,11 @@ ArtBearer::ArtBearer CCommanderInstance::bearerType() const
 	return ArtBearer::COMMANDER;
 	return ArtBearer::COMMANDER;
 }
 }
 
 
+bool CCommanderInstance::gainsLevel() const
+{
+	return experience >= VLC->heroh->reqExp(level+1);
+}
+
 CStackBasicDescriptor::CStackBasicDescriptor()
 CStackBasicDescriptor::CStackBasicDescriptor()
 {
 {
 	type = NULL;
 	type = NULL;

+ 1 - 0
lib/CCreatureSet.h

@@ -103,6 +103,7 @@ public:
 	void giveStackExp (TExpType exp);
 	void giveStackExp (TExpType exp);
 	void levelUp ();
 	void levelUp ();
 
 
+	bool gainsLevel() const; //true if commander has lower level than should upon his experience
 	ui64 getPower() const {return 0;};
 	ui64 getPower() const {return 0;};
 	int getExpRank() const;
 	int getExpRank() const;
 	int getLevel() const OVERRIDE; 
 	int getLevel() const OVERRIDE; 

+ 1 - 1
lib/CGameState.cpp

@@ -3196,7 +3196,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile
 		{
 		{
 			BOOST_FOREACH(const CGObjectInstance *obj, tinfo->visitableObjects)
 			BOOST_FOREACH(const CGObjectInstance *obj, tinfo->visitableObjects)
 			{
 			{
-				if(obj->getPassableness() & 1<<hero->tempOwner.getNum()) //special object instance specific passableness flag - overwrites other accessibility flags
+				if(obj->passableFor(hero->tempOwner)) //special object instance specific passableness flag - overwrites other accessibility flags
 				{
 				{
 					ret = CGPathNode::ACCESSIBLE;
 					ret = CGPathNode::ACCESSIBLE;
 				}
 				}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 345 - 219
lib/CObjectHandler.cpp


+ 47 - 34
lib/CObjectHandler.h

@@ -113,6 +113,13 @@ public:
 	virtual void newTurn() const;
 	virtual void newTurn() const;
 	virtual void initObj(); //synchr
 	virtual void initObj(); //synchr
 	virtual void setProperty(ui8 what, ui32 val);//synchr
 	virtual void setProperty(ui8 what, ui32 val);//synchr
+	
+	//Called when queries created DURING HERO VISIT are resolved
+	//First parameter is always hero that visited object and triggered the query
+	virtual void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const;
+	virtual void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const;
+	virtual void garrisonDialogClosed(const CGHeroInstance *hero) const;
+
 //unified interface, AI helpers
 //unified interface, AI helpers
 	virtual bool wasVisited (PlayerColor player) const;
 	virtual bool wasVisited (PlayerColor player) const;
 	virtual bool wasVisited (const CGHeroInstance * h) const;
 	virtual bool wasVisited (const CGHeroInstance * h) const;
@@ -179,6 +186,7 @@ public:
 	virtual ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used
 	virtual ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used
 	virtual int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
 	virtual int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
 	virtual int getSightRadious() const; //sight distance (should be used if player-owned structure)
 	virtual int getSightRadious() const; //sight distance (should be used if player-owned structure)
+	bool passableFor(PlayerColor color) const;
 	void getSightTiles(boost::unordered_set<int3, ShashInt3> &tiles) const; //returns reference to the set
 	void getSightTiles(boost::unordered_set<int3, ShashInt3> &tiles) const; //returns reference to the set
 	PlayerColor getOwner() const;
 	PlayerColor getOwner() const;
 	void setOwner(PlayerColor ow);
 	void setOwner(PlayerColor ow);
@@ -396,6 +404,8 @@ public:
 	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
 	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
 	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
 	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
 	ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
 	ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
+	std::vector<SecondarySkill> levelUpProposedSkills() const;
+	bool gainsLevel() const; //true if hero has lower level than should upon his experience
 
 
 	//////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////
 
 
@@ -471,10 +481,11 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void newTurn() const override;
 	void newTurn() const override;
 	void setProperty(ui8 what, ui32 val) override;
 	void setProperty(ui8 what, ui32 val) override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
+
 private:
 private:
-	void heroAcceptsCreatures(const CGHeroInstance *h, ui32 answer) const;
-	void fightOver(const CGHeroInstance *h, BattleResult *result) const;
-	void wantsFight(const CGHeroInstance *h, ui32 answer) const;
+	void heroAcceptsCreatures(const CGHeroInstance *h) const;
 };
 };
 
 
 
 
@@ -482,25 +493,26 @@ class DLL_LINKAGE CGVisitableOPH : public CGObjectInstance //objects visitable o
 {
 {
 public:
 public:
 	std::set<ObjectInstanceID> visitors; //ids of heroes who have visited this obj
 	std::set<ObjectInstanceID> visitors; //ids of heroes who have visited this obj
-	si8 ttype; //tree type - used only by trees of knowledge: 0 - give level for free; 1 - take 2000 gold; 2 - take 10 gems
+	TResources treePrice; //used only by trees of knowledge: empty, 2000 gold, 10 gems
 
 
 	const std::string & getHoverText() const override;
 	const std::string & getHoverText() const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void initObj() override;
 	void initObj() override;
 	bool wasVisited (const CGHeroInstance * h) const override;
 	bool wasVisited (const CGHeroInstance * h) const override;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
 		h & static_cast<CGObjectInstance&>(*this);
 		h & static_cast<CGObjectInstance&>(*this);
-		h & visitors & ttype;
+		h & visitors & treePrice;
 	}
 	}
 protected:
 protected:
 	void setPropertyDer(ui8 what, ui32 val) override;//synchr
 	void setPropertyDer(ui8 what, ui32 val) override;//synchr
 private:
 private:
 	void onNAHeroVisit(ObjectInstanceID heroID, bool alreadyVisited) const;
 	void onNAHeroVisit(ObjectInstanceID heroID, bool alreadyVisited) const;
 	///dialog callbacks
 	///dialog callbacks
-	void treeSelected(ObjectInstanceID heroID, int resType, int resVal, TExpType expVal, ui32 result) const;
+	void treeSelected(ObjectInstanceID heroID, ui32 result) const;
 	void schoolSelected(ObjectInstanceID heroID, ui32 which) const;
 	void schoolSelected(ObjectInstanceID heroID, ui32 which) const;
 	void arenaSelected(ObjectInstanceID heroID, int primSkill) const;
 	void arenaSelected(ObjectInstanceID heroID, int primSkill) const;
 };
 };
@@ -662,11 +674,9 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroLeave(const CGHeroInstance * h) const override;
 	void onHeroLeave(const CGHeroInstance * h) const override;
 	void initObj() override;
 	void initObj() override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
 protected:
 protected:
 	void setPropertyDer(ui8 what, ui32 val) override;
 	void setPropertyDer(ui8 what, ui32 val) override;
-private:
-	///dialog callbacks
-	void fightOver(const CGHeroInstance *h, BattleResult *result) const;
 };
 };
 class DLL_LINKAGE CGPandoraBox : public CArmedInstance
 class DLL_LINKAGE CGPandoraBox : public CArmedInstance
 {
 {
@@ -688,6 +698,8 @@ public:
 
 
 	void initObj() override;
 	void initObj() override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -696,10 +708,8 @@ public:
 			& abilities & abilityLevels & artifacts & spells & creatures;
 			& abilities & abilityLevels & artifacts & spells & creatures;
 	}
 	}
 protected:
 protected:
-	void endBattle(const CGHeroInstance *h, BattleResult *result) const;
 	void giveContents(const CGHeroInstance *h, bool afterBattle) const;
 	void giveContents(const CGHeroInstance *h, bool afterBattle) const;
 private:
 private:
-	void open (const CGHeroInstance * h, ui32 accept) const;
 	void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const;
 	void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const;
 	void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const;
 	void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const;
 };
 };
@@ -725,6 +735,10 @@ private:
 
 
 class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
 class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
 {
 {
+	enum Action {
+		FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
+	};
+
 public:
 public:
 	ui32 identifier; //unique code for this monster (used in missions)
 	ui32 identifier; //unique code for this monster (used in missions)
 	si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
 	si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
@@ -735,11 +749,14 @@ public:
 	bool notGrowingTeam; //if true, number of units won't grow
 	bool notGrowingTeam; //if true, number of units won't grow
 	ui64 temppower; //used to handle fractional stack growth for tiny stacks
 	ui64 temppower; //used to handle fractional stack growth for tiny stacks
 
 
+	bool refusedJoining;
 
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	const std::string & getHoverText() const override;
 	const std::string & getHoverText() const override;
 	void initObj() override;
 	void initObj() override;
 	void newTurn() const override;
 	void newTurn() const override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 
 
 	struct DLL_LINKAGE RestoredCreature // info about merging stacks after battle back into one
 	struct DLL_LINKAGE RestoredCreature // info about merging stacks after battle back into one
@@ -754,7 +771,8 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
 		h & static_cast<CArmedInstance&>(*this);
 		h & static_cast<CArmedInstance&>(*this);
-		h & identifier & character & message & resources & gainedArtifact & neverFlees & notGrowingTeam & temppower & restore;
+		h & identifier & character & message & resources & gainedArtifact & neverFlees & notGrowingTeam & temppower;
+		h & refusedJoining & restore;
 	}
 	}
 protected:
 protected:
 	void setPropertyDer(ui8 what, ui32 val) override;
 	void setPropertyDer(ui8 what, ui32 val) override;
@@ -762,7 +780,6 @@ private:
 
 
 	void fight(const CGHeroInstance *h) const;
 	void fight(const CGHeroInstance *h) const;
 	void flee( const CGHeroInstance * h ) const;
 	void flee( const CGHeroInstance * h ) const;
-	void endBattle(BattleResult *result) const;
 	void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
 	void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
 	void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
 	void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
 
 
@@ -815,6 +832,7 @@ public:
 	const std::string & getHoverText() const override;
 	const std::string & getHoverText() const override;
 	void newTurn() const override;
 	void newTurn() const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	int checkDirection() const; //calculates the region of map where monster is placed
 	int checkDirection() const; //calculates the region of map where monster is placed
 	void setObjToKill(); //remember creatures / heroes to kill after they are initialized
 	void setObjToKill(); //remember creatures / heroes to kill after they are initialized
@@ -888,7 +906,7 @@ public:
 
 
 	ui8 getPassableness() const;
 	ui8 getPassableness() const;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void fightOver (const CGHeroInstance *h, BattleResult *result) const;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -904,8 +922,9 @@ public:
 	std::string message;
 	std::string message;
 
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void fightForArt(ui32 agreed, const CGHeroInstance *h) const;
-	void endBattle(BattleResult *result, const CGHeroInstance *h) const;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
+
 	void pick( const CGHeroInstance * h ) const;
 	void pick( const CGHeroInstance * h ) const;
 	void initObj() override;
 	void initObj() override;
 
 
@@ -924,10 +943,10 @@ public:
 
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void initObj() override;
 	void initObj() override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	void collectRes(PlayerColor player) const;
 	void collectRes(PlayerColor player) const;
-	void fightForRes(ui32 agreed, const CGHeroInstance *h) const;
-	void endBattle(BattleResult *result, const CGHeroInstance *h) const;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -943,7 +962,7 @@ public:
 
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void initObj() override;
 	void initObj() override;
-	void chosen(int which, ObjectInstanceID heroID) const;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -972,12 +991,10 @@ class DLL_LINKAGE CGMine : public CArmedInstance
 public:
 public:
 	Res::ERes producedResource;
 	Res::ERes producedResource;
 	ui32 producedQuantity;
 	ui32 producedQuantity;
-
-	void offerLeavingGuards(const CGHeroInstance *h) const;
-	void endBattle(BattleResult *result, PlayerColor attackingPlayer) const;
-	void fight(ui32 agreed, const CGHeroInstance *h) const;
-
+	
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	void flagMine(PlayerColor player) const;
 	void flagMine(PlayerColor player) const;
 	void newTurn() const override;
 	void newTurn() const override;
@@ -1125,13 +1142,12 @@ public:
 	CGBorderGuard() : IQuestObject(){};
 	CGBorderGuard() : IQuestObject(){};
 	void initObj() override;
 	void initObj() override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const;
 	void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const;
 	void getRolloverText (MetaString &text, bool onHover) const;
 	void getRolloverText (MetaString &text, bool onHover) const;
 	bool checkQuest (const CGHeroInstance * h) const;
 	bool checkQuest (const CGHeroInstance * h) const;
 
 
-	void openGate(const CGHeroInstance *h, ui32 accept) const;
-
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
 		h & static_cast<IQuestObject&>(*this);
 		h & static_cast<IQuestObject&>(*this);
@@ -1184,8 +1200,7 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	const std::string & getHoverText() const override;
 	const std::string & getHoverText() const override;
 	void initObj() override;
 	void initObj() override;
-
-	void searchTomb(const CGHeroInstance *h, ui32 accept) const;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -1210,9 +1225,8 @@ class DLL_LINKAGE CBank : public CArmedInstance
 	void newTurn() const override;
 	void newTurn() const override;
 	bool wasVisited (PlayerColor player) const override;
 	bool wasVisited (PlayerColor player) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	virtual void fightGuards (const CGHeroInstance *h, ui32 accept) const;
-	virtual void endBattle (const CGHeroInstance *h, const BattleResult *result) const;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -1231,8 +1245,7 @@ public:
 	const std::string & getHoverText() const override;
 	const std::string & getHoverText() const override;
 	void newTurn() const override {}; //empty, no reset
 	void newTurn() const override {}; //empty, no reset
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	void endBattle (const CGHeroInstance *h, const BattleResult *result) const override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -1270,7 +1283,7 @@ class DLL_LINKAGE CCartographer : public CPlayersVisited
 ///behaviour varies depending on surface and  floor
 ///behaviour varies depending on surface and  floor
 public:
 public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void buyMap (const CGHeroInstance *h, ui32 accept) const;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{

+ 5 - 0
lib/GameConstants.cpp

@@ -98,3 +98,8 @@ CSpell * SpellID::toSpell() const
 
 
 //template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
 //template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
 //template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
 //template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
+
+bool PlayerColor::isValidPlayer() const
+{
+	return num < PLAYER_LIMIT_I;
+}

+ 2 - 0
lib/GameConstants.h

@@ -215,6 +215,8 @@ class PlayerColor : public BaseForID<PlayerColor, ui8>
 	DLL_LINKAGE static const int PLAYER_LIMIT_I = 8; //player limit per map
 	DLL_LINKAGE static const int PLAYER_LIMIT_I = 8; //player limit per map
 	DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map
 	DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map
 
 
+	DLL_LINKAGE bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral)
+
 	friend class CGameInfoCallback;
 	friend class CGameInfoCallback;
 	friend class CNonConstInfoCallback;
 	friend class CNonConstInfoCallback;
 };
 };

+ 6 - 6
lib/IGameCallback.h

@@ -220,11 +220,11 @@ public:
 	virtual void setHoverName(const CGObjectInstance * obj, MetaString * name)=0;
 	virtual void setHoverName(const CGObjectInstance * obj, MetaString * name)=0;
 	virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false)=0;
 	virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false)=0;
 	virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0; 
 	virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0; 
-	virtual void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback)=0;
-	virtual ui32 showBlockingDialog(BlockingDialog *iw) =0; //synchronous version of above //TODO:
+	virtual void showBlockingDialog(BlockingDialog *iw) =0;
 	virtual void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) =0; //cb will be called when player closes garrison window
 	virtual void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) =0; //cb will be called when player closes garrison window
 	virtual void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) =0;
 	virtual void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) =0;
 	virtual void giveResource(PlayerColor player, Res::ERes which, int val)=0;
 	virtual void giveResource(PlayerColor player, Res::ERes which, int val)=0;
+	virtual void giveResources(PlayerColor player, TResources resources)=0;
 
 
 	virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0;
 	virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0;
 	virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) =0;
 	virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) =0;
@@ -247,11 +247,11 @@ public:
 	virtual void showCompInfo(ShowInInfobox * comp)=0;
 	virtual void showCompInfo(ShowInInfobox * comp)=0;
 	virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;
 	virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;
 	virtual void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;
 	virtual void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;
-	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero
-	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used
-	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
+	virtual void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero
+	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false)=0; //if any of armies is hero, hero will be used
+	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
 	virtual void setAmount(ObjectInstanceID objid, ui32 val)=0;
 	virtual void setAmount(ObjectInstanceID objid, ui32 val)=0;
-	virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 instant, PlayerColor asker = PlayerColor::NEUTRAL)=0;
+	virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL)=0;
 	virtual void giveHeroBonus(GiveBonus * bonus)=0;
 	virtual void giveHeroBonus(GiveBonus * bonus)=0;
 	virtual void setMovePoints(SetMovePoints * smp)=0;
 	virtual void setMovePoints(SetMovePoints * smp)=0;
 	virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
 	virtual void setManaPoints(ObjectInstanceID hid, int val)=0;

+ 13 - 8
lib/NetPacks.h

@@ -633,7 +633,7 @@ struct RemoveObject : public CPackForClient //500
 };
 };
 struct TryMoveHero : public CPackForClient //501
 struct TryMoveHero : public CPackForClient //501
 {
 {
-	TryMoveHero(){type = 501;humanKnows=false; attackedFrom = int3(-1, -1, -1);};
+	TryMoveHero(){type = 501;humanKnows=false;};
 	void applyFirstCl(CClient *cl);
 	void applyFirstCl(CClient *cl);
 	void applyCl(CClient *cl);
 	void applyCl(CClient *cl);
 	void applyGs(CGameState *gs);
 	void applyGs(CGameState *gs);
@@ -648,7 +648,7 @@ struct TryMoveHero : public CPackForClient //501
 	EResult result; //uses EResult
 	EResult result; //uses EResult
 	int3 start, end; //h3m format
 	int3 start, end; //h3m format
 	boost::unordered_set<int3, ShashInt3> fowRevealed; //revealed tiles
 	boost::unordered_set<int3, ShashInt3> fowRevealed; //revealed tiles
-	int3 attackedFrom; // Set when stepping into endangered tile.
+	boost::optional<int3> attackedFrom; // Set when stepping into endangered tile.
 
 
 	bool humanKnows; //used locally during applying to client
 	bool humanKnows; //used locally during applying to client
 
 
@@ -1168,7 +1168,7 @@ struct InfoWindow : public CPackForClient //103  - displays simple info window
 namespace ObjProperty
 namespace ObjProperty
 {
 {
 	enum {OWNER = 1, BLOCKVIS = 2, PRIMARY_STACK_COUNT = 3, VISITORS = 4, VISITED = 5, ID = 6, AVAILABLE_CREATURE = 7, SUBID = 8,
 	enum {OWNER = 1, BLOCKVIS = 2, PRIMARY_STACK_COUNT = 3, VISITORS = 4, VISITED = 5, ID = 6, AVAILABLE_CREATURE = 7, SUBID = 8,
-		MONSTER_COUNT = 10, MONSTER_POWER = 11, MONSTER_EXP = 12, MONSTER_RESTORE_TYPE = 13,
+		MONSTER_COUNT = 10, MONSTER_POWER = 11, MONSTER_EXP = 12, MONSTER_RESTORE_TYPE = 13, MONSTER_REFUSED_JOIN,
 	
 	
 		//town-specific
 		//town-specific
 		STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO,  //changing buildings state
 		STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO,  //changing buildings state
@@ -1216,7 +1216,7 @@ struct HeroLevelUp : public Query//2000
 	void applyCl(CClient *cl);
 	void applyCl(CClient *cl);
 	DLL_LINKAGE void applyGs(CGameState *gs);
 	DLL_LINKAGE void applyGs(CGameState *gs);
 
 
-	ObjectInstanceID heroid;
+	const CGHeroInstance *hero;
 	PrimarySkill::PrimarySkill primskill;
 	PrimarySkill::PrimarySkill primskill;
 	ui8 level;
 	ui8 level;
 	std::vector<SecondarySkill> skills;
 	std::vector<SecondarySkill> skills;
@@ -1225,7 +1225,7 @@ struct HeroLevelUp : public Query//2000
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
-		h & queryID & heroid & primskill & level & skills;
+		h & queryID & hero & primskill & level & skills;
 	}
 	}
 };
 };
 
 
@@ -1234,8 +1234,7 @@ struct CommanderLevelUp : public Query
 	void applyCl(CClient *cl);
 	void applyCl(CClient *cl);
 	DLL_LINKAGE void applyGs(CGameState *gs);
 	DLL_LINKAGE void applyGs(CGameState *gs);
 
 
-	ObjectInstanceID heroid; //for commander attached to hero
-	StackLocation sl; //for commander not on the hero?
+	const CGHeroInstance *hero;
 
 
 	std::vector<ui32> skills; //0-5 - secondary skills, val-100 - special skill
 	std::vector<ui32> skills; //0-5 - secondary skills, val-100 - special skill
 
 
@@ -1243,7 +1242,7 @@ struct CommanderLevelUp : public Query
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
-		h & queryID & heroid & sl & skills;
+		h & queryID & hero & skills;
 	}
 	}
 };
 };
 
 
@@ -1302,6 +1301,12 @@ struct BlockingDialog : public Query//2003
 		soundID = 0;
 		soundID = 0;
 	};
 	};
 
 
+	void addResourceComponents(TResources resources)
+	{
+		for(TResources::nziterator i(resources); i.valid(); i++)
+			components.push_back(Component(Component::RESOURCE, i->resType, i->resVal, 0));
+	}
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
 		h & queryID & text & components & player & flags & soundID;
 		h & queryID & text & components & player & flags & soundID;

+ 2 - 2
lib/NetPacksLib.cpp

@@ -994,7 +994,7 @@ DLL_LINKAGE void SetHoverName::applyGs( CGameState *gs )
 
 
 DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs )
 DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs )
 {
 {
-	CGHeroInstance* h = gs->getHero(heroid);
+	CGHeroInstance* h = gs->getHero(hero->id);
 	h->level = level;
 	h->level = level;
 	//specialty
 	//specialty
 	h->Updatespecialty();
 	h->Updatespecialty();
@@ -1002,7 +1002,7 @@ DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs )
 
 
 DLL_LINKAGE void CommanderLevelUp::applyGs (CGameState *gs)
 DLL_LINKAGE void CommanderLevelUp::applyGs (CGameState *gs)
 {
 {
-	CCommanderInstance * commander = gs->getHero(heroid)->commander;
+	CCommanderInstance * commander = gs->getHero(hero->id)->commander;
 	assert (commander);
 	assert (commander);
 	commander->levelUp();
 	commander->levelUp();
 }
 }

+ 3 - 3
lib/ResourceSet.cpp

@@ -92,7 +92,7 @@ void Res::ResourceSet::nziterator::advance()
 {
 {
 	do
 	do
 	{
 	{
-		cur.resType++;
+		vstd::advance(cur.resType, +1);
 	} while(cur.resType < GameConstants::RESOURCE_QUANTITY && !(cur.resVal=rs[cur.resType]));
 	} while(cur.resType < GameConstants::RESOURCE_QUANTITY && !(cur.resVal=rs[cur.resType]));
 
 
 	if(cur.resType >= GameConstants::RESOURCE_QUANTITY)
 	if(cur.resType >= GameConstants::RESOURCE_QUANTITY)
@@ -102,8 +102,8 @@ void Res::ResourceSet::nziterator::advance()
 Res::ResourceSet::nziterator::nziterator(const ResourceSet &RS)
 Res::ResourceSet::nziterator::nziterator(const ResourceSet &RS)
 	: rs(RS)
 	: rs(RS)
 {
 {
-	cur.resType = 0;
-	cur.resVal = rs[0];
+	cur.resType = WOOD;
+	cur.resVal = rs[WOOD];
 
 
 	if(!valid())
 	if(!valid())
 		advance();
 		advance();

+ 10 - 1
lib/ResourceSet.h

@@ -98,6 +98,14 @@ namespace Res
 			return *this;
 			return *this;
 		}
 		}
 
 
+		ResourceSet operator-() const
+		{
+			ResourceSet ret;
+			for(int i = 0; i < size(); i++)
+				ret[i] = -at(i);
+			return ret;
+		}
+
 	// WARNING: comparison operators are used for "can afford" relation: a <= b means that foreach i a[i] <= b[i]
 	// WARNING: comparison operators are used for "can afford" relation: a <= b means that foreach i a[i] <= b[i]
 	// that doesn't work the other way: a > b doesn't mean that a cannot be afforded with b, it's still b can afford a
 	// that doesn't work the other way: a > b doesn't mean that a cannot be afforded with b, it's still b can afford a
 // 		bool operator<(const ResourceSet &rhs)
 // 		bool operator<(const ResourceSet &rhs)
@@ -124,7 +132,8 @@ namespace Res
 		{
 		{
 			struct ResEntry
 			struct ResEntry
 			{
 			{
-				TResourceCap resType, resVal;
+				Res::ERes resType;
+				TResourceCap resVal;
 			} cur;
 			} cur;
 			const ResourceSet &rs;
 			const ResourceSet &rs;
 			void advance();
 			void advance();

+ 15 - 5
lib/logging/CLogger.h

@@ -128,7 +128,7 @@ extern DLL_LINKAGE CLogger * logAi;
 
 
 /// RAII class for tracing the program execution.
 /// RAII class for tracing the program execution.
 /// It prints "Leaving function XYZ" automatically when the object gets destructed.
 /// It prints "Leaving function XYZ" automatically when the object gets destructed.
-class DLL_LINKAGE CTraceLogger
+class DLL_LINKAGE CTraceLogger : boost::noncopyable
 {
 {
 public:
 public:
 	CTraceLogger(const CLogger * logger, const std::string & beginMessage, const std::string & endMessage);
 	CTraceLogger(const CLogger * logger, const std::string & beginMessage, const std::string & endMessage);
@@ -141,10 +141,20 @@ private:
 
 
 /// Macros for tracing the control flow of the application conveniently. If the LOG_TRACE macro is used it should be
 /// Macros for tracing the control flow of the application conveniently. If the LOG_TRACE macro is used it should be
 /// the first statement in the function. Logging traces via this macro have almost no impact when the trace is disabled.
 /// the first statement in the function. Logging traces via this macro have almost no impact when the trace is disabled.
-#define LOG_TRACE(logger) if(logger->isTraceEnabled()) CTraceLogger ctl00(logger, boost::str(boost::format("Entering %s.") % BOOST_CURRENT_FUNCTION), \
-	boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
-#define LOG_TRACE_PARAMS(logger, formatStr, params) if(logger->isTraceEnabled()) CTraceLogger ctl00(logger, boost::str(boost::format("Entering %s: " + \
-	std::string(formatStr) + ".") % BOOST_CURRENT_FUNCTION % params), boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
+/// 
+#define RAII_TRACE(logger, onEntry, onLeave)			\
+	unique_ptr<CTraceLogger> ctl00;						\
+	if(logger->isTraceEnabled())						\
+		ctl00 = make_unique<CTraceLogger>(logger, onEntry, onLeave);
+
+#define LOG_TRACE(logger) RAII_TRACE(logger,								\
+		boost::str(boost::format("Entering %s.") % BOOST_CURRENT_FUNCTION),	\
+		boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
+
+
+#define LOG_TRACE_PARAMS(logger, formatStr, params) RAII_TRACE(logger,		\
+		boost::str(boost::format("Entering %s: " + std::string(formatStr) + ".") % BOOST_CURRENT_FUNCTION % params), \
+		boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
 
 
 /* ---------------------------------------------------------------------------- */
 /* ---------------------------------------------------------------------------- */
 /* Implementation/Detail classes, Private API */
 /* Implementation/Detail classes, Private API */

+ 2 - 2
lib/mapping/CMapEditManager.h

@@ -55,7 +55,7 @@ protected:
 };
 };
 
 
 /// The CMapUndoManager provides the functionality to save operations and undo/redo them.
 /// The CMapUndoManager provides the functionality to save operations and undo/redo them.
-class CMapUndoManager
+class CMapUndoManager : boost::noncopyable
 {
 {
 public:
 public:
 	CMapUndoManager();
 	CMapUndoManager();
@@ -87,7 +87,7 @@ private:
 
 
 /// The map edit manager provides functionality for drawing terrain and placing
 /// The map edit manager provides functionality for drawing terrain and placing
 /// objects on the map.
 /// objects on the map.
-class DLL_LINKAGE CMapEditManager
+class DLL_LINKAGE CMapEditManager : boost::noncopyable
 {
 {
 public:
 public:
 	CMapEditManager(CMap * map);
 	CMapEditManager(CMap * map);

+ 1 - 1
lib/rmg/CMapGenerator.h

@@ -56,7 +56,7 @@ class DLL_LINKAGE CMapGenOptions
 {
 {
 public:
 public:
 	/// The player settings class maps the player color, starting town and human player flag.
 	/// The player settings class maps the player color, starting town and human player flag.
-	class CPlayerSettings
+	class DLL_LINKAGE CPlayerSettings
 	{
 	{
 	public:
 	public:
 		CPlayerSettings();
 		CPlayerSettings();

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 279 - 357
server/CGameHandler.cpp


+ 41 - 27
server/CGameHandler.h

@@ -7,6 +7,8 @@
 #include "../lib/IGameCallback.h"
 #include "../lib/IGameCallback.h"
 #include "../lib/BattleAction.h"
 #include "../lib/BattleAction.h"
 #include "../lib/NetPacks.h"
 #include "../lib/NetPacks.h"
+#include "CQuery.h"
+
 
 
 /*
 /*
  * CGameHandler.h, part of VCMI engine
  * CGameHandler.h, part of VCMI engine
@@ -41,13 +43,12 @@ extern boost::mutex gsm;
 
 
 struct PlayerStatus
 struct PlayerStatus
 {
 {
-	bool makingTurn, engagedIntoBattle;
-	std::set<ui32> queries;
+	bool makingTurn;
 
 
-	PlayerStatus():makingTurn(false),engagedIntoBattle(false){};
+	PlayerStatus():makingTurn(false){};
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
-		h & makingTurn & engagedIntoBattle & queries;
+		h & makingTurn;
 	}
 	}
 };
 };
 class PlayerStatuses
 class PlayerStatuses
@@ -59,11 +60,8 @@ public:
 
 
 	void addPlayer(PlayerColor player);
 	void addPlayer(PlayerColor player);
 	PlayerStatus operator[](PlayerColor player);
 	PlayerStatus operator[](PlayerColor player);
-	int getQueriesCount(PlayerColor player); //returns 0 if there is no such player
 	bool checkFlag(PlayerColor player, bool PlayerStatus::*flag);
 	bool checkFlag(PlayerColor player, bool PlayerStatus::*flag);
 	void setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val);
 	void setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val);
-	void addQuery(PlayerColor player, ui32 id);
-	void removeQuery(PlayerColor player, ui32 id);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
 		h & players;
 		h & players;
@@ -85,7 +83,6 @@ class CGameHandler : public IGameCallback, CBattleInfoCallback
 {
 {
 private:
 private:
 	void makeStackDoNothing(const CStack * next);
 	void makeStackDoNothing(const CStack * next);
-	bool isAllowedExchangeForQuery(ObjectInstanceID id1, ObjectInstanceID id2);
 public:
 public:
 	CVCMIServer *s;
 	CVCMIServer *s;
 	std::map<PlayerColor, CConnection*> connections; //player color -> connection to client with interface of that player
 	std::map<PlayerColor, CConnection*> connections; //player color -> connection to client with interface of that player
@@ -95,25 +92,22 @@ public:
 	//queries stuff
 	//queries stuff
 	boost::recursive_mutex gsm;
 	boost::recursive_mutex gsm;
 	ui32 QID;
 	ui32 QID;
+	Queries queries;
 
 
 	//TODO get rid of cfunctionlist (or similar) and use serialziable callback structure
 	//TODO get rid of cfunctionlist (or similar) and use serialziable callback structure
 	std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs
 	std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs
-	std::map<ui32, std::pair<ObjectInstanceID, ObjectInstanceID> > allowedExchanges;
 
 
-	bool isBlockedByQueries(const CPack *pack, int packType, PlayerColor player); 
+	bool isValidObject(const CGObjectInstance *obj) const;
+	bool isBlockedByQueries(const CPack *pack, PlayerColor player); 
 	bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2);
 	bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2);
-	bool isAllowedArrangePack(const ArrangeStacks *pack);
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 	int moveStack(int stack, BattleHex dest); //returned value - travelled distance
 	int moveStack(int stack, BattleHex dest); //returned value - travelled distance
-	void startBattle(const CArmedInstance *armies[2], int3 tile, const CGHeroInstance *heroes[2], bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero
 	void runBattle();
 	void runBattle();
 	void checkLossVictory(PlayerColor player);
 	void checkLossVictory(PlayerColor player);
 	void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all
 	void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all
 	void getLossVicMessage(PlayerColor player, si8 standard, bool victory, InfoWindow &out) const;
 	void getLossVicMessage(PlayerColor player, si8 standard, bool victory, InfoWindow &out) const;
 
 
 	////used only in endBattle - don't touch elsewhere
 	////used only in endBattle - don't touch elsewhere
-	boost::function<void(BattleResult*)> * battleEndCallback;
-	//const CArmedInstance * bEndArmy1, * bEndArmy2;
 	bool visitObjectAfterVictory;
 	bool visitObjectAfterVictory;
 	//
 	//
 	void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle
 	void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle
@@ -137,11 +131,12 @@ public:
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE;
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE;
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE; 
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE; 
 	//void showInfoDialog(InfoWindow *iw) OVERRIDE;
 	//void showInfoDialog(InfoWindow *iw) OVERRIDE;
-	void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback) OVERRIDE;
-	ui32 showBlockingDialog(BlockingDialog *iw) OVERRIDE; //synchronous version of above
+
+	void showBlockingDialog(BlockingDialog *iw) OVERRIDE; 
 	void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE;
 	void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE;
 	void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE;
 	void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE;
 	void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE;
 	void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE;
+	void giveResources(PlayerColor player, TResources resources) OVERRIDE;
 
 
 	void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE;
 	void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE;
 	void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE;
 	void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE;
@@ -165,11 +160,11 @@ public:
 	void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;
 	void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;
 	void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;
 	void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;
 	//bool removeArtifact(const CArtifact* art, int hid) OVERRIDE;
 	//bool removeArtifact(const CArtifact* art, int hid) OVERRIDE;
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL) OVERRIDE; //use hero=NULL for no hero
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) OVERRIDE; //for hero<=>neutral army
+	void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = NULL) OVERRIDE; //use hero=NULL for no hero
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) OVERRIDE; //for hero<=>neutral army
 	void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE;
 	void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE;
-	bool moveHero(ObjectInstanceID hid, int3 dst, ui8 instant, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE;
+	bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE;
 	void giveHeroBonus(GiveBonus * bonus) OVERRIDE;
 	void giveHeroBonus(GiveBonus * bonus) OVERRIDE;
 	void setMovePoints(SetMovePoints * smp) OVERRIDE;
 	void setMovePoints(SetMovePoints * smp) OVERRIDE;
 	void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE;
 	void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE;
@@ -179,7 +174,6 @@ public:
 	//////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////
 	void useScholarSkill(ObjectInstanceID hero1, ObjectInstanceID hero2);
 	void useScholarSkill(ObjectInstanceID hero1, ObjectInstanceID hero2);
 	void setPortalDwelling(const CGTownInstance * town, bool forced, bool clear);
 	void setPortalDwelling(const CGTownInstance * town, bool forced, bool clear);
-	bool tryAttackingGuard(const int3 &guardPos, const CGHeroInstance * h);
 	void visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h);
 	void visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h);
 	bool teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui8 source, PlayerColor asker = PlayerColor::NEUTRAL);
 	bool teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui8 source, PlayerColor asker = PlayerColor::NEUTRAL);
 	void vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h);
 	void vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h);
@@ -187,7 +181,8 @@ public:
 	void levelUpHero(const CGHeroInstance * hero);//initial call - check if hero have remaining levelups & handle them
 	void levelUpHero(const CGHeroInstance * hero);//initial call - check if hero have remaining levelups & handle them
 	void levelUpCommander (const CCommanderInstance * c, int skill); //secondary skill 1 to 6, special skill : skill - 100
 	void levelUpCommander (const CCommanderInstance * c, int skill); //secondary skill 1 to 6, special skill : skill - 100
 	void levelUpCommander (const CCommanderInstance * c);
 	void levelUpCommander (const CCommanderInstance * c);
-	void afterBattleCallback(); // called after level-ups are finished
+
+	void expGiven(const CGHeroInstance *hero); //triggers needed level-ups, handles also commander of this hero
 	//////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////
 
 
 	void commitPackage(CPackForClient *pack) OVERRIDE;
 	void commitPackage(CPackForClient *pack) OVERRIDE;
@@ -233,6 +228,7 @@ public:
 	void handleTownEvents(CGTownInstance *town, NewTurn &n);
 	void handleTownEvents(CGTownInstance *town, NewTurn &n);
 	bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
 	bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
 	void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
 	void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
+	void objectVisitEnded(const CObjectVisitQuery &query);
 	void engageIntoBattle( PlayerColor player );
 	void engageIntoBattle( PlayerColor player );
 	bool dig(const CGHeroInstance *h);
 	bool dig(const CGHeroInstance *h);
 	bool castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos);
 	bool castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos);
@@ -240,15 +236,11 @@ public:
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
-		h & QID & states;
+		h & QID & states & finishingBattle;
 	}
 	}
 
 
-	ui32 getQueryResult(PlayerColor player, int queryID);
 	void sendMessageToAll(const std::string &message);
 	void sendMessageToAll(const std::string &message);
 	void sendMessageTo(CConnection &c, const std::string &message);
 	void sendMessageTo(CConnection &c, const std::string &message);
-	void applyAndAsk(Query * sel, PlayerColor player, boost::function<void(ui32)> &callback);
-	void prepareNewQuery(Query * queryPack, PlayerColor player, const boost::function<void(ui32)> &callback = 0); //generates unique query id and writes it to the pack; blocks the player till query is answered (then callback is called)
-	void ask(Query * sel, PlayerColor player, const CFunctionList<void(ui32)> &callback);
 	void sendToAllClients(CPackForClient * info);
 	void sendToAllClients(CPackForClient * info);
 	void sendAndApply(CPackForClient * info);
 	void sendAndApply(CPackForClient * info);
 	void applyAndSend(CPackForClient * info);
 	void applyAndSend(CPackForClient * info);
@@ -257,6 +249,28 @@ public:
 	void sendAndApply(SetResources * info);
 	void sendAndApply(SetResources * info);
 	void sendAndApply(NewStructures * info);
 	void sendAndApply(NewStructures * info);
 
 
+	struct FinishingBattleHelper
+	{
+		FinishingBattleHelper();
+		FinishingBattleHelper(shared_ptr<const CBattleQuery> Query, bool Duel, int RemainingBattleQueriesCount);
+
+		shared_ptr<const CBattleQuery> query;
+		const CGHeroInstance *winnerHero, *loserHero;
+		PlayerColor victor, loser;
+		bool duel;
+
+		int remainingBattleQueriesCount;
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & query & winnerHero & loserHero & victor & loser & duel & remainingBattleQueriesCount;
+		}
+	};
+
+	unique_ptr<FinishingBattleHelper> finishingBattle;
+
+	void battleAfterLevelUp(const BattleResult &result);
+
 	void run(bool resume);
 	void run(bool resume);
 	void newTurn();
 	void newTurn();
 	void handleAttackBeforeCasting (const BattleAttack & bat);
 	void handleAttackBeforeCasting (const BattleAttack & bat);

+ 1 - 0
server/CMakeLists.txt

@@ -6,6 +6,7 @@ include_directories(${Boost_INCLUDE_DIRS})
 
 
 set(server_SRCS
 set(server_SRCS
         CGameHandler.cpp
         CGameHandler.cpp
+        CQuery.cpp
         CVCMIServer.cpp
         CVCMIServer.cpp
         NetPacksServer.cpp
         NetPacksServer.cpp
 )
 )

+ 342 - 0
server/CQuery.cpp

@@ -0,0 +1,342 @@
+#include "StdInc.h"
+#include "CQuery.h"
+#include "CGameHandler.h"
+#include "..\lib\BattleState.h"
+
+boost::mutex Queries::mx;
+
+template <typename Container>
+std::string formatContainer(const Container &c, std::string delimeter=", ", std::string opener="(", std::string closer=")")
+{
+	std::string ret = opener;
+	auto itr = boost::begin(c);
+	if(itr != boost::end(c))
+	{
+		ret += boost::lexical_cast<std::string>(*itr);
+		while(++itr != boost::end(c))
+		{
+			ret += delimeter;
+			ret += boost::lexical_cast<std::string>(*itr);
+		}
+	}
+	ret += closer;
+	return ret;
+}
+
+std::ostream & operator<<(std::ostream &out, const CQuery &query)
+{
+	return out << query.toString();
+}
+
+std::ostream & operator<<(std::ostream &out, QueryPtr query)
+{
+	return out << "[" << query.get() << "] " << query->toString();
+}
+
+CQuery::CQuery(void)
+{
+	boost::unique_lock<boost::mutex> l(Queries::mx);
+
+	static TQueryID QID = 1;
+
+	queryID = QID++;
+	logGlobal->traceStream() << "Created a new query with id " << queryID;
+}
+
+
+CQuery::~CQuery(void)
+{
+	logGlobal->traceStream() << "Destructed the query with id " << queryID;
+}
+
+void CQuery::addPlayer(PlayerColor color)
+{
+	if(color.isValidPlayer())
+	{
+		players.push_back(color);
+	}
+}
+
+std::string CQuery::toString() const
+{
+	std::string ret = boost::str(boost::format("A query of type %s and qid=%d affecting players %s") % typeid(*this).name() % queryID % formatContainer(players));
+	return ret;
+}
+
+bool CQuery::endsByPlayerAnswer() const
+{
+	return false;
+}
+
+void CQuery::onRemoval(CGameHandler *gh, PlayerColor color)
+{
+}
+
+bool CQuery::blocksPack(const CPack *pack) const
+{
+	return false;
+}
+
+void CQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
+{
+}
+
+void CQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
+{
+	gh->queries.popQuery(*this);
+}
+
+CObjectVisitQuery::CObjectVisitQuery(const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile)
+	: visitedObject(Obj), visitingHero(Hero), tile(Tile)
+{
+	addPlayer(Hero->tempOwner);
+}
+
+bool CObjectVisitQuery::blocksPack(const CPack *pack) const 
+{
+	//During the visit itself ALL actions are blocked.
+	//(However, the visit may trigger a query above that'll pass some.)
+	return true; 
+}
+
+void CObjectVisitQuery::onRemoval(CGameHandler *gh, PlayerColor color)
+{
+	gh->objectVisitEnded(*this);
+}
+
+void CObjectVisitQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
+{
+	//Object may have been removed and deleted.
+	if(gh->isValidObject(visitedObject))
+		topQuery->notifyObjectAboutRemoval(*this);
+
+	gh->queries.popQuery(*this);
+}
+
+void Queries::popQuery(PlayerColor player, QueryPtr query)
+{
+	LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
+	if(topQuery(player) != query)
+	{
+		logGlobal->traceStream() << "Cannot remove, not a top!";
+		return;
+	}
+
+	queries[player] -= query;
+	auto nextQuery = topQuery(player);
+
+	query->onRemoval(gh, player);
+
+	//Exposure on query below happens only if removal didnt trigger any new query
+	if(nextQuery && nextQuery == topQuery(player))
+	{
+		nextQuery->onExposure(gh, query);
+	}
+}
+
+void Queries::popQuery(const CQuery &query)
+{
+	LOG_TRACE_PARAMS(logGlobal, "query='%s'", query);
+
+	assert(query.players.size());
+	BOOST_FOREACH(auto player, query.players)
+	{
+		auto top = topQuery(player);
+		if(top.get() == &query)
+			popQuery(top);
+		else
+			logGlobal->traceStream() << "Cannot remove query " << query;
+	}
+}
+
+void Queries::popQuery(QueryPtr query)
+{
+	BOOST_FOREACH(auto player, query->players)
+		popQuery(player, query);
+}
+
+void Queries::addQuery(QueryPtr query)
+{
+	BOOST_FOREACH(auto player, query->players)
+		addQuery(player, query);
+}
+
+void Queries::addQuery(PlayerColor player, QueryPtr query)
+{
+	LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
+	queries[player].push_back(query);
+}
+
+QueryPtr Queries::topQuery(PlayerColor player)
+{
+	return vstd::backOrNull(queries[player]);
+}
+
+void Queries::popIfTop(QueryPtr query)
+{
+	popIfTop(*query);
+}
+
+void Queries::popIfTop(const CQuery &query)
+{
+	BOOST_FOREACH(PlayerColor color, query.players)
+		if(topQuery(color).get() == &query)
+			popQuery(color, topQuery(color));
+}
+
+std::vector<shared_ptr<const CQuery>> Queries::allQueries() const
+{
+	std::vector<shared_ptr<const CQuery>> ret;
+	BOOST_FOREACH(auto &playerQueries, queries)
+		BOOST_FOREACH(auto &query, playerQueries.second)
+			ret.push_back(query);
+
+	return ret;
+}
+
+std::vector<shared_ptr<CQuery>> Queries::allQueries()
+{
+	//TODO code duplication with const function :(
+	std::vector<shared_ptr<CQuery>> ret;
+	BOOST_FOREACH(auto &playerQueries, queries)
+		BOOST_FOREACH(auto &query, playerQueries.second)
+		ret.push_back(query);
+
+	return ret;
+}
+
+void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const 
+{
+	assert(result);
+	objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
+}
+
+CBattleQuery::CBattleQuery(const BattleInfo *Bi)
+{
+	belligerents[0] = Bi->belligerents[0];
+	belligerents[1] = Bi->belligerents[1];
+
+	bi = Bi;
+
+	BOOST_FOREACH(PlayerColor side, bi->sides)
+		addPlayer(side);
+}
+
+CBattleQuery::CBattleQuery()
+{
+
+}
+
+bool CBattleQuery::blocksPack(const CPack *pack) const 
+{
+	return !dynamic_cast<const MakeAction*>(pack) && !dynamic_cast<const MakeCustomAction*>(pack);
+}
+
+void CBattleQuery::onRemoval(CGameHandler *gh, PlayerColor color)
+{
+	gh->battleAfterLevelUp(*result);
+}
+
+void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const 
+{
+	objectVisit.visitedObject->garrisonDialogClosed(objectVisit.visitingHero);
+}
+
+CGarrisonDialogQuery::CGarrisonDialogQuery(const CArmedInstance *up, const CArmedInstance *down)
+{
+	exchangingArmies[0] = up;
+	exchangingArmies[1] = down;
+
+	addPlayer(up->tempOwner);
+	addPlayer(down->tempOwner);
+}
+
+bool CGarrisonDialogQuery::blocksPack(const CPack *pack) const 
+{
+	if(auto stacks = dynamic_cast<const ArrangeStacks*>(pack))
+	{
+		std::set<ObjectInstanceID> ourIds;
+		ourIds.insert(this->exchangingArmies[0]->id);
+		ourIds.insert(this->exchangingArmies[1]->id);
+
+		return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2);
+	}
+
+	return CDialogQuery::blocksPack(pack);
+}
+
+void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const 
+{
+	assert(answer);
+	objectVisit.visitedObject->blockingDialogAnswered(objectVisit.visitingHero, *answer);
+}
+
+CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd)
+{
+	this->bd = bd;
+	addPlayer(bd.player);
+}
+
+CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu)
+{
+	hlu = Hlu;
+	addPlayer(hlu.hero->tempOwner);
+}
+
+void CHeroLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color)
+{
+	assert(answer);
+	logGlobal->traceStream() << "Completing hero level-up query. " << hlu.hero->getHoverText() << " gains skill " << answer;
+	gh->levelUpHero(hlu.hero, hlu.skills[*answer]);
+}
+
+CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu)
+{
+	clu = Clu;
+	addPlayer(clu.hero->tempOwner);
+}
+
+void CCommanderLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color)
+{
+	assert(answer);
+	logGlobal->traceStream() << "Completing commander level-up query. Commander of hero " << clu.hero->getHoverText() << " gains skill " << answer;
+	gh->levelUpCommander(clu.hero->commander, clu.skills[*answer]);
+}
+
+bool CDialogQuery::endsByPlayerAnswer() const 
+{
+	return true;
+}
+
+bool CDialogQuery::blocksPack(const CPack *pack) const 
+{
+	//We accept only query replies from correct player
+	if(auto reply = dynamic_cast<const QueryReply *>(pack))
+	{
+		return !vstd::contains(players, reply->player);
+	}
+
+	return true;
+}
+
+CHeroMovementQuery::CHeroMovementQuery(const TryMoveHero &Tmh, const CGHeroInstance *Hero, bool VisitDestAfterVictory)
+	: tmh(Tmh), visitDestAfterVictory(VisitDestAfterVictory), hero(Hero)
+{
+	players.push_back(hero->tempOwner);
+}
+
+void CHeroMovementQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
+{
+	assert(players.size() == 1);
+
+	if(visitDestAfterVictory && hero->tempOwner == players[0]) //hero still alive, so he won with the guard
+		//TODO what if there were H4-like escape? we should also check pos
+	{
+		logGlobal->traceStream() << "Hero " << hero->name << " after victory over guard finishes visit to " << tmh.end;
+		//finish movement
+		visitDestAfterVictory = false;
+		gh->visitObjectOnTile(*gh->getTile(CGHeroInstance::convertPosition(tmh.end, false)), hero);
+	}
+
+	gh->queries.popIfTop(*this);
+}

+ 177 - 0
server/CQuery.h

@@ -0,0 +1,177 @@
+#pragma once
+#include "..\lib\GameConstants.h"
+#include "..\lib\int3.h"
+#include "..\lib\NetPacks.h"
+
+class CGObjectInstance;
+class CGHeroInstance;
+class CArmedInstance;
+struct CPack;
+class CGameHandler;
+class CObjectVisitQuery;
+struct TryMoveHero;
+class CQuery;
+
+typedef si32 TQueryID;
+typedef shared_ptr<CQuery> QueryPtr;
+
+// This class represents any kind of prolonged interaction that may need to do something special after it is over.
+// It does not necessarily has to be "query" requiring player action, it can be also used internally within server.
+// Examples:
+// - all kinds of blocking dialog windows 
+// - battle 
+// - object visit
+// - hero movement
+// Queries can cause another queries, forming a stack of queries for each player. Eg: hero movement -> object visit -> dialog.
+class CQuery
+{
+protected:
+	void addPlayer(PlayerColor color);
+public:
+	std::vector<PlayerColor> players; //players that are affected (often "blocked") by query
+	TQueryID queryID;
+
+	CQuery(void);
+
+
+	virtual bool blocksPack(const CPack *pack) const; //query can block attempting actions by player. Eg. he can't move hero during the battle.
+
+	virtual bool endsByPlayerAnswer() const; //query is removed after player gives answer (like dialogs)
+	virtual void onRemoval(CGameHandler *gh, PlayerColor color); //called after query is removed from stack
+	virtual void onExposure(CGameHandler *gh, QueryPtr topQuery);//called when query immediately above is removed and this is exposed (becomes top)
+	virtual std::string toString() const;
+
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const;
+
+	virtual ~CQuery(void);
+
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & players & queryID;
+	}
+};
+
+std::ostream &operator<<(std::ostream &out, const CQuery &query);
+std::ostream &operator<<(std::ostream &out, QueryPtr query);
+
+//Created when hero visits object.
+//Removed when query above is resolved (or immediately after visit if no queries were created)
+class CObjectVisitQuery : public CQuery
+{
+public:
+	const CGObjectInstance *visitedObject;
+	const CGHeroInstance *visitingHero;
+	int3 tile; //may be different than hero pos -> eg. visit via teleport
+
+	CObjectVisitQuery(const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile);
+
+	virtual bool blocksPack(const CPack *pack) const OVERRIDE;
+	virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
+	virtual void onExposure(CGameHandler *gh, QueryPtr topQuery) OVERRIDE;
+};
+
+class CBattleQuery : public CQuery
+{
+public:
+	std::array<const CArmedInstance *,2> belligerents;
+
+	const BattleInfo *bi;
+	boost::optional<BattleResult> result;
+
+	CBattleQuery();
+	CBattleQuery(const BattleInfo *Bi); //TODO
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
+	virtual bool blocksPack(const CPack *pack) const OVERRIDE;
+	virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
+};
+
+//Created when hero attempts move and something happens
+//(not necessarily position change, could be just an object interaction).
+class CHeroMovementQuery : public CQuery
+{
+public:
+	TryMoveHero tmh;
+	bool visitDestAfterVictory; //if hero moved to guarded tile and it should be visited once guard is defeated
+	const CGHeroInstance *hero;
+
+	virtual void onExposure(CGameHandler *gh, QueryPtr topQuery);
+
+	CHeroMovementQuery(const TryMoveHero &Tmh, const CGHeroInstance *Hero, bool VisitDestAfterVictory = false);
+};
+
+class CDialogQuery : public CQuery
+{
+public:
+	boost::optional<ui32> answer;
+	virtual bool endsByPlayerAnswer() const OVERRIDE;
+	virtual bool blocksPack(const CPack *pack) const OVERRIDE;
+};
+
+class CGarrisonDialogQuery : public CDialogQuery
+{
+public:
+	std::array<const CArmedInstance *,2> exchangingArmies;
+
+	CGarrisonDialogQuery(const CArmedInstance *up, const CArmedInstance *down);
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
+	virtual bool blocksPack(const CPack *pack) const OVERRIDE;
+};
+
+//yes/no and component selection dialogs
+class CBlockingDialogQuery : public CDialogQuery
+{
+public:
+	BlockingDialog bd; //copy of pack... debug purposes
+
+	CBlockingDialogQuery(const BlockingDialog &bd);
+
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
+};
+
+class CHeroLevelUpDialogQuery : public CDialogQuery
+{
+public:
+	CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu);
+
+	virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
+
+	HeroLevelUp hlu;
+};
+
+
+class CCommanderLevelUpDialogQuery : public CDialogQuery
+{
+public:
+	CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu);
+
+	virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
+
+	CommanderLevelUp clu;
+};
+
+struct Queries
+{
+private:
+	void addQuery(PlayerColor player, QueryPtr query);
+	void popQuery(PlayerColor player, QueryPtr query);
+
+	std::map<PlayerColor, std::vector<QueryPtr>> queries; //player => stack of queries
+
+public:
+	CGameHandler *gh;
+	static boost::mutex mx;
+
+	void addQuery(QueryPtr query);
+	void popQuery(const CQuery &query);
+	void popQuery(QueryPtr query);
+	void popIfTop(const CQuery &query); //removes this query if it is at the top (otherwise, do nothing)
+	void popIfTop(QueryPtr query); //removes this query if it is at the top (otherwise, do nothing)
+
+	QueryPtr topQuery(PlayerColor player);
+
+	std::vector<shared_ptr<const CQuery>> allQueries() const;
+	std::vector<shared_ptr<CQuery>> allQueries();
+	//void removeQuery
+
+};

+ 2 - 2
server/NetPacksServer.cpp

@@ -69,8 +69,8 @@ bool EndTurn::applyGh( CGameHandler *gh )
 {
 {
 	PlayerColor player = GS(gh)->currentPlayer;
 	PlayerColor player = GS(gh)->currentPlayer;
 	ERROR_IF_NOT(player);
 	ERROR_IF_NOT(player);
-	if(gh->states.checkFlag(player, &PlayerStatus::engagedIntoBattle))
-		COMPLAIN_AND_RETURN("Cannot end turn when in battle!");
+	if(gh->queries.topQuery(player))
+		COMPLAIN_AND_RETURN("Cannot end turn before resolving queries!");
 
 
 	gh->states.setFlag(GS(gh)->currentPlayer,&PlayerStatus::makingTurn,false);
 	gh->states.setFlag(GS(gh)->currentPlayer,&PlayerStatus::makingTurn,false);
 	return true;
 	return true;

+ 2 - 0
server/VCMI_server.vcxproj

@@ -145,6 +145,7 @@
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="CGameHandler.cpp" />
     <ClCompile Include="CGameHandler.cpp" />
+    <ClCompile Include="CQuery.cpp" />
     <ClCompile Include="CVCMIServer.cpp" />
     <ClCompile Include="CVCMIServer.cpp" />
     <ClCompile Include="NetPacksServer.cpp" />
     <ClCompile Include="NetPacksServer.cpp" />
     <ClCompile Include="StdInc.cpp">
     <ClCompile Include="StdInc.cpp">
@@ -158,6 +159,7 @@
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\Global.h" />
     <ClInclude Include="..\Global.h" />
     <ClInclude Include="CGameHandler.h" />
     <ClInclude Include="CGameHandler.h" />
+    <ClInclude Include="CQuery.h" />
     <ClInclude Include="CVCMIServer.h" />
     <ClInclude Include="CVCMIServer.h" />
     <ClInclude Include="StdInc.h" />
     <ClInclude Include="StdInc.h" />
   </ItemGroup>
   </ItemGroup>

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů