浏览代码

* fixed crash related to cammander's SPELL_AFTER_ATTACK spell id not initialized properly (text id was resolved on copy of bonus)
* fixed duels, added no-GUI mode for automatic AI testing
* minor things

Michał W. Urbańczyk 12 年之前
父节点
当前提交
6a88604937

+ 17 - 2
AI/BattleAI/BattleAI.cpp

@@ -354,15 +354,16 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 		if(cb->battleCanCastSpell())
 		if(cb->battleCanCastSpell())
 			attemptCastingSpell();
 			attemptCastingSpell();
 
 
+		if(auto action = considerFleeingOrSurrendering())
+			return *action;
+
 		if(cb->battleGetStacks(CBattleInfoEssentials::ONLY_ENEMY).empty())
 		if(cb->battleGetStacks(CBattleInfoEssentials::ONLY_ENEMY).empty())
 		{
 		{
 			//We apparently won battle by casting spell, return defend... (accessing cb may cause trouble)
 			//We apparently won battle by casting spell, return defend... (accessing cb may cause trouble)
 			return BattleAction::makeDefend(stack);
 			return BattleAction::makeDefend(stack);
 		}
 		}
 
 
-		ThreatMap threatsToUs(stack);
 		PotentialTargets targets(stack);
 		PotentialTargets targets(stack);
-
 		if(targets.possibleAttacks.size())
 		if(targets.possibleAttacks.size())
 		{
 		{
 			auto hlp = targets.bestAction();
 			auto hlp = targets.bestAction();
@@ -375,6 +376,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 		{
 		{
 			if(stack->waited())
 			if(stack->waited())
 			{
 			{
+				ThreatMap threatsToUs(stack);
 				auto dists = cbc->battleGetDistances(stack);
 				auto dists = cbc->battleGetDistances(stack);
 				const EnemyInfo &ei= *range::min_element(targets.unreachableEnemies, boost::bind(isCloser, _1, _2, boost::ref(dists)));
 				const EnemyInfo &ei= *range::min_element(targets.unreachableEnemies, boost::bind(isCloser, _1, _2, boost::ref(dists)));
 				if(distToNearestNeighbour(ei.s->position, dists) < GameConstants::BFIELD_SIZE)
 				if(distToNearestNeighbour(ei.s->position, dists) < GameConstants::BFIELD_SIZE)
@@ -749,3 +751,16 @@ std::vector<BattleHex> CBattleAI::getTargetsToConsider( const CSpell *spell ) co
 		return cbc->battleGetPossibleTargets(playerID, spell);
 		return cbc->battleGetPossibleTargets(playerID, spell);
 	}
 	}
 }
 }
+
+boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
+{
+	if(cb->battleCanSurrender(playerID))
+	{
+
+	}
+	if(cb->battleCanFlee())
+	{
+
+	}
+	return boost::none;
+}

+ 2 - 0
AI/BattleAI/BattleAI.h

@@ -42,6 +42,8 @@ public:
 	BattleAction goTowards(const CStack * stack, BattleHex hex );
 	BattleAction goTowards(const CStack * stack, BattleHex hex );
 	BattleAction useCatapult(const CStack * stack);
 	BattleAction useCatapult(const CStack * stack);
 
 
+	boost::optional<BattleAction> considerFleeingOrSurrendering();
+
 	void attemptCastingSpell();
 	void attemptCastingSpell();
 	std::vector<BattleHex> getTargetsToConsider(const CSpell *spell) const;
 	std::vector<BattleHex> getTargetsToConsider(const CSpell *spell) const;
 };
 };

+ 2 - 2
client/CCreatureWindow.cpp

@@ -239,8 +239,8 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 				else
 				else
 				{
 				{
 					selectableSkill->pos = Rect (95, 256, 55, 55); //TODO: scroll
 					selectableSkill->pos = Rect (95, 256, 55, 55); //TODO: scroll
-					Bonus b = CGI->creh->skillRequirements[option-100].first; 
-					bonusItems.push_back (new CBonusItem (genRect(0, 0, 251, 57), stack->bonusToString(&b, false), stack->bonusToString(&b, true), stack->bonusToGraphics(&b)));
+					const Bonus *b = CGI->creh->skillRequirements[option-100].first; 
+					bonusItems.push_back (new CBonusItem (genRect(0, 0, 251, 57), stack->bonusToString(b, false), stack->bonusToString(b, true), stack->bonusToGraphics(b)));
 					selectableBonuses.push_back (selectableSkill); //insert these before other bonuses
 					selectableBonuses.push_back (selectableSkill); //insert these before other bonuses
 				}
 				}
 			}
 			}

+ 79 - 44
client/CMT.cpp

@@ -47,6 +47,7 @@
 #endif
 #endif
 #include "../lib/CDefObjInfoHandler.h"
 #include "../lib/CDefObjInfoHandler.h"
 #include "../lib/UnlockGuard.h"
 #include "../lib/UnlockGuard.h"
+#include "CMT.h"
 
 
 #if __MINGW32__
 #if __MINGW32__
 #undef main
 #undef main
@@ -76,6 +77,7 @@ static boost::thread *mainGUIThread;
 std::queue<SDL_Event> events;
 std::queue<SDL_Event> events;
 boost::mutex eventsM;
 boost::mutex eventsM;
 
 
+bool gNoGUI = false;
 static bool gOnlyAI = false;
 static bool gOnlyAI = false;
 //static bool setResolution = false; //set by event handling thread after resolution is adjusted
 //static bool setResolution = false; //set by event handling thread after resolution is adjusted
 
 
@@ -146,20 +148,23 @@ void init()
     logGlobal->infoStream()<<"Initializing VCMI_Lib: "<<tmh.getDiff();
     logGlobal->infoStream()<<"Initializing VCMI_Lib: "<<tmh.getDiff();
 
 
 	pomtime.getDiff();
 	pomtime.getDiff();
-	CCS->curh = new CCursorHandler;
-	graphics = new Graphics(); // should be before curh->init()
+	if(!gNoGUI)
+	{
+		CCS->curh = new CCursorHandler;
+		graphics = new Graphics(); // should be before curh->init()
 
 
-	CCS->curh->initCursor();
-	CCS->curh->show();
-    logGlobal->infoStream()<<"Screen handler: "<<pomtime.getDiff();
-	pomtime.getDiff();
+		CCS->curh->initCursor();
+		CCS->curh->show();
+		logGlobal->infoStream()<<"Screen handler: "<<pomtime.getDiff();
+		pomtime.getDiff();
 
 
-	graphics->loadHeroAnims();
-    logGlobal->infoStream()<<"\tMain graphics: "<<tmh.getDiff();
-    logGlobal->infoStream()<<"Initializing game graphics: "<<tmh.getDiff();
+		graphics->loadHeroAnims();
+		logGlobal->infoStream()<<"\tMain graphics: "<<tmh.getDiff();
+		logGlobal->infoStream()<<"Initializing game graphics: "<<tmh.getDiff();
 
 
-	CMessage::init();
-    logGlobal->infoStream()<<"Message handler: "<<tmh.getDiff();
+		CMessage::init();
+		logGlobal->infoStream()<<"Message handler: "<<tmh.getDiff();
+	}
 }
 }
 
 
 static void prog_version(void)
 static void prog_version(void)
@@ -219,7 +224,8 @@ int main(int argc, char** argv)
 		("version,v", "display version information and exit")
 		("version,v", "display version information and exit")
 		("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
 		("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
 		("start", po::value<std::string>(), "starts game from saved StartInfo file")
 		("start", po::value<std::string>(), "starts game from saved StartInfo file")
-		("onlyAI", "runs without GUI, all players will be default AI")
+		("onlyAI", "runs without human player, all players will be default AI")
+		("noGUI", "runs without GUI, implies --onlyAI")
 		("oneGoodAI", "puts one default AI and the rest will be EmptyAI")
 		("oneGoodAI", "puts one default AI and the rest will be EmptyAI")
 		("autoSkip", "automatically skip turns in GUI")
 		("autoSkip", "automatically skip turns in GUI")
 		("disable-video", "disable video player")
 		("disable-video", "disable video player")
@@ -249,6 +255,11 @@ int main(int argc, char** argv)
 		prog_version();
 		prog_version();
 		return 0;
 		return 0;
 	}
 	}
+	if(vm.count("noGUI"))
+	{
+		gNoGUI = true;
+		vm["onlyAI"];
+	}
 
 
 	//Set environment vars to make window centered. Sometimes work, sometimes not. :/
 	//Set environment vars to make window centered. Sometimes work, sometimes not. :/
 	putenv((char*)"SDL_VIDEO_WINDOW_POS");
 	putenv((char*)"SDL_VIDEO_WINDOW_POS");
@@ -303,16 +314,7 @@ int main(int argc, char** argv)
     logGlobal->infoStream() << NAME;
     logGlobal->infoStream() << NAME;
 
 
 	srand ( time(NULL) );
 	srand ( time(NULL) );
-
-	CCS = new CClientState;
-	CGI = new CGameInfo; //contains all global informations about game (texts, lodHandlers, map handler etc.)
-
-	if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO))
-	{
-        logGlobal->errorStream()<<"Something was wrong: "<< SDL_GetError();
-		exit(-1);
-	}
-	atexit(SDL_Quit);
+	
 
 
 	const JsonNode& video = settings["video"];
 	const JsonNode& video = settings["video"];
 	const JsonNode& res = video["screenRes"];
 	const JsonNode& res = video["screenRes"];
@@ -328,15 +330,26 @@ int main(int argc, char** argv)
 		exit(EXIT_FAILURE);
 		exit(EXIT_FAILURE);
 	}
 	}
 
 
-	setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), video["fullscreen"].Bool());
+	if(!gNoGUI)
+	{
+		if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO))
+		{
+			logGlobal->errorStream()<<"Something was wrong: "<< SDL_GetError();
+			exit(-1);
+		}
+		atexit(SDL_Quit);
+		setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), video["fullscreen"].Bool());
+		logGlobal->infoStream() <<"\tInitializing screen: "<<pomtime.getDiff();
+	}
 
 
-    logGlobal->infoStream() <<"\tInitializing screen: "<<pomtime.getDiff();
 
 
+	CCS = new CClientState;
+	CGI = new CGameInfo; //contains all global informations about game (texts, lodHandlers, map handler etc.)
 	// Initialize video
 	// Initialize video
 #if DISABLE_VIDEO
 #if DISABLE_VIDEO
 	CCS->videoh = new CEmptyVideoPlayer;
 	CCS->videoh = new CEmptyVideoPlayer;
 #else
 #else
-	if (!vm.count("disable-video"))
+	if (!gNoGUI && !vm.count("disable-video"))
 		CCS->videoh = new CVideoPlayer;
 		CCS->videoh = new CVideoPlayer;
 	else
 	else
 		CCS->videoh = new CEmptyVideoPlayer;
 		CCS->videoh = new CEmptyVideoPlayer;
@@ -344,13 +357,18 @@ int main(int argc, char** argv)
 
 
     logGlobal->infoStream()<<"\tInitializing video: "<<pomtime.getDiff();
     logGlobal->infoStream()<<"\tInitializing video: "<<pomtime.getDiff();
 
 
+
+
 	//we can properly play intro only in the main thread, so we have to move loading to the separate thread
 	//we can properly play intro only in the main thread, so we have to move loading to the separate thread
 	boost::thread loading(init);
 	boost::thread loading(init);
 
 
-	if(!vm.count("battle") && !vm.count("nointro"))
-		playIntro();
+	if(!gNoGUI )
+	{
+		if(!vm.count("battle") && !vm.count("nointro"))
+			playIntro();
+		SDL_FillRect(screen,NULL,0);
+	}
 
 
-	SDL_FillRect(screen,NULL,0);
 	CSDL_Ext::update(screen);
 	CSDL_Ext::update(screen);
 	loading.join();
 	loading.join();
     logGlobal->infoStream()<<"Initialization of VCMI (together): "<<total.getDiff();
     logGlobal->infoStream()<<"Initialization of VCMI (together): "<<total.getDiff();
@@ -387,8 +405,17 @@ int main(int argc, char** argv)
 		si->playerInfos[PlayerColor(1)].color = PlayerColor(1);
 		si->playerInfos[PlayerColor(1)].color = PlayerColor(1);
 		startGame(si);
 		startGame(si);
 	}
 	}
-	mainGUIThread = new boost::thread(&CGuiHandler::run, boost::ref(GH));
-	listenForEvents();
+
+	if(!gNoGUI)
+	{
+		mainGUIThread = new boost::thread(&CGuiHandler::run, boost::ref(GH));
+		listenForEvents();
+	}
+	else
+	{
+		while(true)
+			boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+	}
 
 
 	return 0;
 	return 0;
 }
 }
@@ -822,20 +849,7 @@ static void listenForEvents()
 		if (ret == 0 || (ev.type==SDL_QUIT) ||
 		if (ret == 0 || (ev.type==SDL_QUIT) ||
 			(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
 			(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
 		{
 		{
-			if (client)
-				client->endGame();
-			if (mainGUIThread) 
-			{
-				GH.terminate = true;
-				mainGUIThread->join();
-				delete mainGUIThread;
-				mainGUIThread = NULL;
-			}
-			delete console;
-			console = NULL;
-			SDL_Delay(750);
-			SDL_Quit();
-            std::cout << "Ending...";
+			handleQuit();
 			break;
 			break;
 		}
 		}
 		else if(LOCPLINT && ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
 		else if(LOCPLINT && ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
@@ -929,3 +943,24 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
 
 
 		client->connectionHandler = new boost::thread(&CClient::run, client);
 		client->connectionHandler = new boost::thread(&CClient::run, client);
 }
 }
+
+void handleQuit()
+{
+	if (client)
+		client->endGame();
+	if (mainGUIThread) 
+	{
+		GH.terminate = true;
+		mainGUIThread->join();
+		delete mainGUIThread;
+		mainGUIThread = NULL;
+	}
+	delete console;
+	console = NULL;
+	boost::this_thread::sleep(boost::posix_time::milliseconds(750));
+	if(!gNoGUI)
+		SDL_Quit();
+
+	std::cout << "Ending...";
+	exit(0);
+}

+ 5 - 1
client/CMT.h

@@ -2,4 +2,8 @@
 
 
 extern SDL_Surface *screen;      // main screen surface
 extern SDL_Surface *screen;      // main screen surface
 extern SDL_Surface *screen2;     // and hlp surface (used to store not-active interfaces layer)
 extern SDL_Surface *screen2;     // and hlp surface (used to store not-active interfaces layer)
-extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
+extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
+
+extern bool gNoGUI; //if true there is no client window and game is silently played between AIs
+
+void handleQuit();

+ 14 - 10
client/Client.cpp

@@ -34,6 +34,7 @@
 #include "../lib/CScriptingModule.h"
 #include "../lib/CScriptingModule.h"
 #include "../lib/RegisterTypes.h"
 #include "../lib/RegisterTypes.h"
 #include "gui/CGuiHandler.h"
 #include "gui/CGuiHandler.h"
+#include "CMT.h"
 
 
 extern std::string NAME;
 extern std::string NAME;
 namespace intpr = boost::interprocess;
 namespace intpr = boost::interprocess;
@@ -419,15 +420,18 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 
 
 	if(si->mode == StartInfo::DUEL)
 	if(si->mode == StartInfo::DUEL)
 	{
 	{
-		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
-		CPlayerInterface *p = new CPlayerInterface(PlayerColor::NEUTRAL); //TODO: check if neutral really works -- was -1, but CPlayerInterface seems to cooperate with this value not too well
-		p->observerInDuelMode = true;
-		battleints[PlayerColor::UNFLAGGABLE] = playerint[PlayerColor::UNFLAGGABLE] = p;
-		privilagedBattleEventReceivers.push_back(p);
-		GH.curInt = p;
-		auto cb = make_shared<CCallback>(gs, boost::optional<PlayerColor>(), this);
-		battleCallbacks[PlayerColor::NEUTRAL] = callbacks[PlayerColor::NEUTRAL] = cb;
-		p->init(cb.get());
+		if(!gNoGUI)
+		{
+			boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
+			CPlayerInterface *p = new CPlayerInterface(PlayerColor::NEUTRAL);
+			p->observerInDuelMode = true;
+			battleints[PlayerColor::UNFLAGGABLE] = playerint[PlayerColor::UNFLAGGABLE] = p;
+			privilagedBattleEventReceivers.push_back(p);
+			GH.curInt = p;
+			auto cb = make_shared<CCallback>(gs, boost::optional<PlayerColor>(), this);
+			battleCallbacks[PlayerColor::NEUTRAL] = callbacks[PlayerColor::NEUTRAL] = cb;
+			p->init(cb.get());
+		}
 		battleStarted(gs->curB);
 		battleStarted(gs->curB);
 	}
 	}
 	else
 	else
@@ -616,7 +620,7 @@ void CClient::battleStarted(const BattleInfo * info)
 	else
 	else
 		def = NULL;
 		def = NULL;
 
 
-	if(att || def || gs->scenarioOps->mode == StartInfo::DUEL)
+	if(!gNoGUI && (att || def || gs->scenarioOps->mode == StartInfo::DUEL))
 	{
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
 		new CBattleInterface(info->belligerents[0], info->belligerents[1], info->heroes[0], info->heroes[1],
 		new CBattleInterface(info->belligerents[0], info->belligerents[1], info->heroes[0], info->heroes[1],

+ 2 - 3
client/NetPacksClient.cpp

@@ -27,6 +27,7 @@
 #include "../lib/BattleState.h"
 #include "../lib/BattleState.h"
 #include "../lib/GameConstants.h"
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
 #include "gui/CGuiHandler.h"
+#include "CMT.h"
 
 
 //macros to avoid code duplication - calls given method with given arguments if interface for specific player is present
 //macros to avoid code duplication - calls given method with given arguments if interface for specific player is present
 //awaiting variadic templates...
 //awaiting variadic templates...
@@ -705,9 +706,7 @@ void BattleResultsApplied::applyCl( CClient *cl )
 	INTERFACE_CALL_IF_PRESENT(PlayerColor::UNFLAGGABLE, battleResultsApplied);
 	INTERFACE_CALL_IF_PRESENT(PlayerColor::UNFLAGGABLE, battleResultsApplied);
 	if(GS(cl)->initialOpts->mode == StartInfo::DUEL)
 	if(GS(cl)->initialOpts->mode == StartInfo::DUEL)
 	{
 	{
-		cl->terminate = true;
-		CloseServer cs;
-		*cl->serv << &cs;
+		handleQuit();
 	}
 	}
 }
 }
 
 

+ 2 - 2
client/battle/CBattleInterface.cpp

@@ -204,8 +204,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	bOptions = new CAdventureMapButton (CGI->generaltexth->zelp[381].first, CGI->generaltexth->zelp[381].second, boost::bind(&CBattleInterface::bOptionsf,this), 3, 561, "icm003.def", SDLK_o);
 	bOptions = new CAdventureMapButton (CGI->generaltexth->zelp[381].first, CGI->generaltexth->zelp[381].second, boost::bind(&CBattleInterface::bOptionsf,this), 3, 561, "icm003.def", SDLK_o);
 	bSurrender = new CAdventureMapButton (CGI->generaltexth->zelp[379].first, CGI->generaltexth->zelp[379].second, boost::bind(&CBattleInterface::bSurrenderf,this), 54, 561, "icm001.def", SDLK_s);
 	bSurrender = new CAdventureMapButton (CGI->generaltexth->zelp[379].first, CGI->generaltexth->zelp[379].second, boost::bind(&CBattleInterface::bSurrenderf,this), 54, 561, "icm001.def", SDLK_s);
 	bFlee = new CAdventureMapButton (CGI->generaltexth->zelp[380].first, CGI->generaltexth->zelp[380].second, boost::bind(&CBattleInterface::bFleef,this), 105, 561, "icm002.def", SDLK_r);
 	bFlee = new CAdventureMapButton (CGI->generaltexth->zelp[380].first, CGI->generaltexth->zelp[380].second, boost::bind(&CBattleInterface::bFleef,this), 105, 561, "icm002.def", SDLK_r);
-	bFlee->block(!curInt->cb->battleCanFlee());
-	bSurrender->block(curInt->cb->battleGetSurrenderCost() < 0);
+	bFlee->block(!curInt->cb->getMyColor() || !curInt->cb->battleCanFlee());
+	bSurrender->block(!curInt->cb->getMyColor() || curInt->cb->battleGetSurrenderCost() < 0);
 	bAutofight  = new CAdventureMapButton (CGI->generaltexth->zelp[382].first, CGI->generaltexth->zelp[382].second, boost::bind(&CBattleInterface::bAutofightf,this), 157, 561, "icm004.def", SDLK_a);
 	bAutofight  = new CAdventureMapButton (CGI->generaltexth->zelp[382].first, CGI->generaltexth->zelp[382].second, boost::bind(&CBattleInterface::bAutofightf,this), 157, 561, "icm004.def", SDLK_a);
 	bSpell = new CAdventureMapButton (CGI->generaltexth->zelp[385].first, CGI->generaltexth->zelp[385].second, boost::bind(&CBattleInterface::bSpellf,this), 645, 561, "icm005.def", SDLK_c);
 	bSpell = new CAdventureMapButton (CGI->generaltexth->zelp[385].first, CGI->generaltexth->zelp[385].second, boost::bind(&CBattleInterface::bSpellf,this), 645, 561, "icm005.def", SDLK_c);
 	bSpell->block(true);
 	bSpell->block(true);

+ 9 - 1
lib/CBattleCallback.cpp

@@ -942,6 +942,11 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
 		multBonus *= 0.5;
 		multBonus *= 0.5;
 	}
 	}
 
 
+
+	// TODO attack on petrified unit 50%
+	// psychic elementals versus mind immune units 50%
+	// blinded unit retaliates
+
 	minDmg *= additiveBonus * multBonus;
 	minDmg *= additiveBonus * multBonus;
 	maxDmg *= additiveBonus * multBonus;
 	maxDmg *= additiveBonus * multBonus;
 
 
@@ -2268,7 +2273,10 @@ TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose /*= MINE_AN
 {
 {
 	TStacks ret;
 	TStacks ret;
 	RETURN_IF_NOT_BATTLE(ret);
 	RETURN_IF_NOT_BATTLE(ret);
-	ASSERT_IF_CALLED_WITH_PLAYER
+	if(whose != MINE_AND_ENEMY)
+	{
+		ASSERT_IF_CALLED_WITH_PLAYER
+	}
 	vstd::copy_if(battleGetAllStacks(), std::back_inserter(ret), [=](const CStack *s) -> bool
 	vstd::copy_if(battleGetAllStacks(), std::back_inserter(ret), [=](const CStack *s) -> bool
 	{
 	{
 		const bool ownerMatches = (whose == MINE_AND_ENEMY)
 		const bool ownerMatches = (whose == MINE_AND_ENEMY)

+ 2 - 2
lib/CBonusTypeHandler.cpp

@@ -128,7 +128,7 @@ CBonusTypeHandler::~CBonusTypeHandler()
 	//dtor
 	//dtor
 }
 }
 
 
-std::string CBonusTypeHandler::bonusToString(Bonus *bonus, const IBonusBearer *bearer, bool description) const
+std::string CBonusTypeHandler::bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const
 {
 {
 	auto getValue = [=](const std::string &name) -> std::string
 	auto getValue = [=](const std::string &name) -> std::string
 	{
 	{
@@ -171,7 +171,7 @@ std::string CBonusTypeHandler::bonusToString(Bonus *bonus, const IBonusBearer *b
 	return text;	
 	return text;	
 }
 }
 
 
-std::string CBonusTypeHandler::bonusToGraphics(Bonus* bonus) const
+std::string CBonusTypeHandler::bonusToGraphics(const Bonus* bonus) const
 {
 {
 	std::string fileName;
 	std::string fileName;
 	bool fullPath = false;
 	bool fullPath = false;

+ 2 - 2
lib/CBonusTypeHandler.h

@@ -75,8 +75,8 @@ public:
 	CBonusTypeHandler();
 	CBonusTypeHandler();
 	virtual ~CBonusTypeHandler();
 	virtual ~CBonusTypeHandler();
 	
 	
-	std::string bonusToString(Bonus *bonus, const IBonusBearer *bearer, bool description) const override;
-	std::string bonusToGraphics(Bonus *bonus) const override;
+	std::string bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const override;
+	std::string bonusToGraphics(const Bonus *bonus) const override;
 	
 	
 	void load();
 	void load();
 	void load(const JsonNode& config);
 	void load(const JsonNode& config);

+ 2 - 2
lib/CCreatureHandler.cpp

@@ -212,8 +212,8 @@ void CCreatureHandler::loadCommanders()
 
 
 	BOOST_FOREACH (auto ability, config["abilityRequirements"].Vector())
 	BOOST_FOREACH (auto ability, config["abilityRequirements"].Vector())
 	{
 	{
-		std::pair <Bonus, std::pair <ui8, ui8> > a;
-		a.first = *JsonUtils::parseBonus (ability["ability"].Vector());
+		std::pair <Bonus*, std::pair <ui8, ui8> > a;
+		a.first = JsonUtils::parseBonus (ability["ability"].Vector());
 		a.second.first = ability["skills"].Vector()[0].Float();
 		a.second.first = ability["skills"].Vector()[0].Float();
 		a.second.second = ability["skills"].Vector()[1].Float();
 		a.second.second = ability["skills"].Vector()[1].Float();
 		skillRequirements.push_back (a);
 		skillRequirements.push_back (a);

+ 1 - 1
lib/CCreatureHandler.h

@@ -181,7 +181,7 @@ public:
 	//Commanders
 	//Commanders
 	BonusList commanderLevelPremy; //bonus values added with each level-up
 	BonusList commanderLevelPremy; //bonus values added with each level-up
 	std::vector< std::vector <ui8> > skillLevels; //how much of a bonus will be given to commander with every level. SPELL_POWER also gives CASTS and RESISTANCE
 	std::vector< std::vector <ui8> > skillLevels; //how much of a bonus will be given to commander with every level. SPELL_POWER also gives CASTS and RESISTANCE
-	std::vector <std::pair <Bonus, std::pair <ui8, ui8> > > skillRequirements; // first - Bonus, second - which two skills are needed to use it
+	std::vector <std::pair <Bonus*, std::pair <ui8, ui8> > > skillRequirements; // first - Bonus, second - which two skills are needed to use it
 
 
 	void deserializationFix();
 	void deserializationFix();
 	CreatureID pickRandomMonster(const boost::function<int()> &randGen = 0, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any
 	CreatureID pickRandomMonster(const boost::function<int()> &randGen = 0, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any

+ 2 - 2
lib/CCreatureSet.cpp

@@ -565,7 +565,7 @@ void CStackInstance::setType(const CCreature *c)
 	if(type)
 	if(type)
 		attachTo(const_cast<CCreature*>(type));
 		attachTo(const_cast<CCreature*>(type));
 }
 }
-std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
+std::string CStackInstance::bonusToString(const Bonus *bonus, bool description) const
 {
 {
 	if(Bonus::MAGIC_RESISTANCE == bonus->type)
 	if(Bonus::MAGIC_RESISTANCE == bonus->type)
 	{
 	{
@@ -578,7 +578,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
 	
 	
 }
 }
 
 
-std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
+std::string CStackInstance::bonusToGraphics(const Bonus *bonus) const
 {
 {
 	return VLC->getBth()->bonusToGraphics(bonus);
 	return VLC->getBth()->bonusToGraphics(bonus);
 }
 }

+ 2 - 2
lib/CCreatureSet.h

@@ -56,8 +56,8 @@ public:
 	}
 	}
 
 
 	//overrides CBonusSystemNode
 	//overrides CBonusSystemNode
-	std::string bonusToString(Bonus *bonus, bool description) const override; // how would bonus description look for this particular type of node
-	std::string bonusToGraphics(Bonus *bonus) const; //file name of graphics from StackSkills , in future possibly others
+	std::string bonusToString(const Bonus *bonus, bool description) const override; // how would bonus description look for this particular type of node
+	std::string bonusToGraphics(const Bonus *bonus) const; //file name of graphics from StackSkills , in future possibly others
 
 
 	virtual ui64 getPower() const;
 	virtual ui64 getPower() const;
 	int getQuantityID() const;
 	int getQuantityID() const;

+ 1 - 1
lib/HeroBonus.h

@@ -619,7 +619,7 @@ public:
 	//bool isLimitedOnUs(Bonus *b) const; //if bonus should be removed from list acquired from this node
 	//bool isLimitedOnUs(Bonus *b) const; //if bonus should be removed from list acquired from this node
 
 
 	void popBonuses(const CSelector &s);
 	void popBonuses(const CSelector &s);
-	virtual std::string bonusToString(Bonus *bonus, bool description) const {return "";}; //description or bonus name
+	virtual std::string bonusToString(const Bonus *bonus, bool description) const {return "";}; //description or bonus name
 	virtual std::string nodeName() const;
 	virtual std::string nodeName() const;
 
 
 	void deserializationFix();
 	void deserializationFix();

+ 2 - 2
lib/IBonusTypeHandler.h

@@ -20,6 +20,6 @@ class IBonusTypeHandler
 public:
 public:
 	virtual ~IBonusTypeHandler(){};
 	virtual ~IBonusTypeHandler(){};
 
 
-	virtual std::string bonusToString(Bonus *bonus, const IBonusBearer *bearer, bool description) const = 0;
-	virtual std::string bonusToGraphics(Bonus *bonus) const = 0;
+	virtual std::string bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const = 0;
+	virtual std::string bonusToGraphics(const Bonus *bonus) const = 0;
 };
 };

+ 30 - 5
server/CGameHandler.cpp

@@ -293,7 +293,7 @@ void CGameHandler::levelUpCommander (const CCommanderInstance * c, int skill)
 	else if (skill >= 100)
 	else if (skill >= 100)
 	{
 	{
 		scp.which = SetCommanderProperty::SPECIAL_SKILL;
 		scp.which = SetCommanderProperty::SPECIAL_SKILL;
-		scp.accumulatedBonus = VLC->creh->skillRequirements[skill-100].first;
+		scp.accumulatedBonus = *VLC->creh->skillRequirements[skill-100].first;
 		scp.additionalInfo = skill; //unnormalized
 		scp.additionalInfo = skill; //unnormalized
 		sendAndApply (&scp);
 		sendAndApply (&scp);
 	}
 	}
@@ -435,17 +435,22 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 		return nullptr;
 		return nullptr;
 	};
 	};
 
 
-	const auto battleQuery = findBattleQuery();
+	auto battleQuery = findBattleQuery();
 	if(!battleQuery)
 	if(!battleQuery)
+	{
 		logGlobal->errorStream() << "Cannot find battle query!";
 		logGlobal->errorStream() << "Cannot find battle query!";
+		if(gs->initialOpts->mode == StartInfo::DUEL)
+		{
+			battleQuery = make_shared<CBattleQuery>(gs->curB);
+		}
+	}
 	if(battleQuery != queries.topQuery(gs->curB->sides[0]))
 	if(battleQuery != queries.topQuery(gs->curB->sides[0]))
 		complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0]) + " although in battle has no battle query at the top!");
 		complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0]) + " although in battle has no battle query at the top!");
-
+		
 	battleQuery->result = *battleResult.data;
 	battleQuery->result = *battleResult.data;
 
 
 	//Check how many battle queries were created (number of players blocked by battle)
 	//Check how many battle queries were created (number of players blocked by battle)
-	const int queriedPlayers = boost::count(queries.allQueries(), battleQuery); 
-
+	const int queriedPlayers = battleQuery ? boost::count(queries.allQueries(), battleQuery) : 0; 
 	finishingBattle = make_unique<FinishingBattleHelper>(battleQuery, gs->initialOpts->mode == StartInfo::DUEL, queriedPlayers);
 	finishingBattle = make_unique<FinishingBattleHelper>(battleQuery, gs->initialOpts->mode == StartInfo::DUEL, queriedPlayers);
 
 
 
 
@@ -592,7 +597,13 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 	}
 	}
 
 
 	if(finishingBattle->duel)
 	if(finishingBattle->duel)
+	{
+		BattleResultsApplied resultsApplied;
+		resultsApplied.player1 = finishingBattle->victor;
+		resultsApplied.player2 = finishingBattle->loser;
+		sendAndApply(&resultsApplied);
 		return;
 		return;
+	}
 	
 	
 	cab1.takeFromArmy(this); cab2.takeFromArmy(this); //take casualties after battle is deleted
 	cab1.takeFromArmy(this); cab2.takeFromArmy(this); //take casualties after battle is deleted
 
 
@@ -826,6 +837,12 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
 			{
 			{
 				boost::unique_lock<boost::mutex> lock(*c.rmx);
 				boost::unique_lock<boost::mutex> lock(*c.rmx);
 				c >> player >> requestID >> pack; //get the package
 				c >> player >> requestID >> pack; //get the package
+
+				if(!pack)
+				{
+					logGlobal ->errorStream() << boost::format("Received a null package marked as request %d from player %d") % requestID % player;
+				}
+
 				packType = typeList.getTypeID(pack); //get the id of type
 				packType = typeList.getTypeID(pack); //get the id of type
 
 
                 logGlobal->traceStream() << boost::format("Received client message (request %d by player %d) of type with ID=%d (%s).\n")
                 logGlobal->traceStream() << boost::format("Received client message (request %d by player %d) of type with ID=%d (%s).\n")
@@ -1427,6 +1444,8 @@ void CGameHandler::newTurn()
 }
 }
 void CGameHandler::run(bool resume)
 void CGameHandler::run(bool resume)
 {
 {
+	LOG_TRACE_PARAMS(logGlobal, "resume=%d", resume);
+
 	using namespace boost::posix_time;
 	using namespace boost::posix_time;
 	BOOST_FOREACH(CConnection *cc, conns)
 	BOOST_FOREACH(CConnection *cc, conns)
 	{
 	{
@@ -1468,6 +1487,12 @@ void CGameHandler::run(bool resume)
 	if(gs->scenarioOps->mode == StartInfo::DUEL)
 	if(gs->scenarioOps->mode == StartInfo::DUEL)
 	{
 	{
 		runBattle();
 		runBattle();
+		end2 = true;
+
+
+		while(conns.size() && (*conns.begin())->isOpen())
+			boost::this_thread::sleep(boost::posix_time::milliseconds(5)); //give time client to close socket
+
 		return;
 		return;
 	}
 	}
 
 

+ 2 - 2
server/CGameHandler.h

@@ -265,7 +265,7 @@ public:
 		FinishingBattleHelper();
 		FinishingBattleHelper();
 		FinishingBattleHelper(shared_ptr<const CBattleQuery> Query, bool Duel, int RemainingBattleQueriesCount);
 		FinishingBattleHelper(shared_ptr<const CBattleQuery> Query, bool Duel, int RemainingBattleQueriesCount);
 
 
-		shared_ptr<const CBattleQuery> query;
+		//shared_ptr<const CBattleQuery> query;
 		const CGHeroInstance *winnerHero, *loserHero;
 		const CGHeroInstance *winnerHero, *loserHero;
 		PlayerColor victor, loser;
 		PlayerColor victor, loser;
 		bool duel;
 		bool duel;
@@ -274,7 +274,7 @@ public:
 
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
 		{
-			h & query & winnerHero & loserHero & victor & loser & duel & remainingBattleQueriesCount;
+			h & /*query & */winnerHero & loserHero & victor & loser & duel & remainingBattleQueriesCount;
 		}
 		}
 	};
 	};
 
 

+ 4 - 0
server/CQuery.cpp

@@ -179,6 +179,10 @@ QueryPtr Queries::topQuery(PlayerColor player)
 
 
 void Queries::popIfTop(QueryPtr query)
 void Queries::popIfTop(QueryPtr query)
 {
 {
+	LOG_TRACE_PARAMS(logGlobal, "query='%d'", query);
+	if(!query)
+		logGlobal->errorStream() << "The query is nullptr! Ignoring.";
+
 	popIfTop(*query);
 	popIfTop(*query);
 }
 }