浏览代码

Various fixes and refactorings. Restored client project to solution.

Michał W. Urbańczyk 14 年之前
父节点
当前提交
2b18e0b821

+ 24 - 24
AI/GeniusAI/BattleLogic.cpp

@@ -239,30 +239,30 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 	typedef SCreatureCasualties const std::pair<int, SCreatureCasualties>::* CreaPairPtr;
 	
 	// sort max damage
-	std::sort(m_statMaxDamage.begin(), m_statMaxDamage.end(),
-		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
-	// sort min damage
-	std::sort(m_statMinDamage.begin(), m_statMinDamage.end(),
-		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
-	// sort max speed
-	std::sort(m_statMaxSpeed.begin(), m_statMaxSpeed.end(),
-		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
-	// sort distance
-	std::sort(m_statDistance.begin(), m_statDistance.end(),
-		bind((IntPtr)&creature_stat::value_type::second, _1) < bind((IntPtr)&creature_stat::value_type::second, _2));
-	// sort distance from shooters
-	std::sort(m_statDistanceFromShooters.begin(), m_statDistanceFromShooters.end(),
-		bind((IntPtr)&creature_stat::value_type::second, _1) < bind((IntPtr)&creature_stat::value_type::second, _2));
-	// sort hit points
-	std::sort(m_statHitPoints.begin(), m_statHitPoints.end(),
-		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
-	// sort casualties
-	std::sort(m_statCasualties.begin(), m_statCasualties.end(),
-		bind((CreaPtr)&creature_stat_casualties::value_type::second_type::damage_max, 
-			bind((CreaPairPtr)&creature_stat_casualties::value_type::second, _1))
-		>
-		bind((CreaPtr)&creature_stat_casualties::value_type::second_type::damage_max,
-			bind((CreaPairPtr)&creature_stat_casualties::value_type::second, _2)));
+// 	std::sort(m_statMaxDamage.begin(), m_statMaxDamage.end(),
+// 		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
+// 	// sort min damage
+// 	std::sort(m_statMinDamage.begin(), m_statMinDamage.end(),
+// 		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
+// 	// sort max speed
+// 	std::sort(m_statMaxSpeed.begin(), m_statMaxSpeed.end(),
+// 		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
+// 	// sort distance
+// 	std::sort(m_statDistance.begin(), m_statDistance.end(),
+// 		bind((IntPtr)&creature_stat::value_type::second, _1) < bind((IntPtr)&creature_stat::value_type::second, _2));
+// 	// sort distance from shooters
+// 	std::sort(m_statDistanceFromShooters.begin(), m_statDistanceFromShooters.end(),
+// 		bind((IntPtr)&creature_stat::value_type::second, _1) < bind((IntPtr)&creature_stat::value_type::second, _2));
+// 	// sort hit points
+// 	std::sort(m_statHitPoints.begin(), m_statHitPoints.end(),
+// 		bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
+// 	// sort casualties
+// 	std::sort(m_statCasualties.begin(), m_statCasualties.end(),
+// 		bind((CreaPtr)&creature_stat_casualties::value_type::second_type::damage_max, 
+// 			bind((CreaPairPtr)&creature_stat_casualties::value_type::second, _1))
+// 		>
+// 		bind((CreaPtr)&creature_stat_casualties::value_type::second_type::damage_max,
+// 			bind((CreaPairPtr)&creature_stat_casualties::value_type::second, _2)));
 }
 
 BattleAction CBattleLogic::MakeDecision(int stackID)

+ 1 - 0
CCallback.cpp

@@ -272,6 +272,7 @@ void CCallback::buildBoat( const IShipyard *obj )
 CCallback::CCallback( CGameState * GS, int Player, CClient *C ) 
 	:CBattleCallback(GS, Player, C)
 {
+	cl = C;
 	waitTillRealize = false;
 }
 

+ 2 - 27
CCallback.h

@@ -11,6 +11,7 @@
 #endif
 
 #include "lib/IGameCallback.h"
+#include "lib/CBattleCallback.h"
 
 /*
  * CCallback.h, part of VCMI engine
@@ -37,15 +38,6 @@ struct CGPath;
 struct CPathsInfo;
 struct CPack;
 
-class IBattleCallback
-{
-public:
-	bool waitTillRealize; //if true, request functions will return after they are realized by server
-	bool unlockGsWhenWaiting;//if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback!
-	//battle
-	virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
-	virtual bool battleMakeTacticAction(BattleAction * action) =0; // performs tactic phase actions
-};
 
 class IGameActionCallback
 {
@@ -83,29 +75,12 @@ public:
 	virtual void buildBoat(const IShipyard *obj) = 0;
 };
 
-struct CPack;
-
-class CBattleCallback : public IBattleCallback, public CBattleInfoCallback
-{
-protected:
-	void sendRequest(const CPack *request);
-	CClient *cl;
-	//virtual bool hasAccess(int playerId) const;
-
-public:
-	CBattleCallback(CGameState *GS, int Player, CClient *C);
-	int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack
-	bool battleMakeTacticAction(BattleAction * action) OVERRIDE; // performs tactic phase actions
-
-	friend class CCallback;
-	friend class CClient;
-};
-
 class CCallback : public CPlayerSpecificInfoCallback, public IGameActionCallback, public CBattleCallback
 {
 private:
 	CCallback(CGameState * GS, int Player, CClient *C);
 public:
+	CClient *cl;
 	//client-specific functionalities (pathfinding)
 	virtual bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret); //DEPRACATED!!!
 	virtual const CGPathNode *getPathInfo(int3 tile); //uses main, client pathfinder info

+ 1 - 1
Odpalarka/main.cpp

@@ -7,7 +7,7 @@ int main()
 	boost::thread t(boost::bind(std::system, "VCMI_server.exe b1.json StupidAI StupidAI"));
 	boost::thread tt(boost::bind(std::system, "VCMI_BattleAiHost.exe"));
 	boost::thread ttt(boost::bind(std::system, "VCMI_BattleAiHost.exe"));
-	boost::thread tttt(boost::bind(std::system, "VCMI_BattleAiHost.exe"));
+	//boost::thread tttt(boost::bind(std::system, "VCMI_BattleAiHost.exe"));
 	//boost::this_thread::sleep(boost::posix_time::seconds(5));
 
 	t.join();

+ 0 - 50
VCMI_BattleAiHost/BattleCallback.cpp

@@ -1,50 +0,0 @@
-#include "BattleCallback.h"
-#include "Client.h"
-#include "../lib/CGameState.h"
-#include "../lib/BattleState.h"
-#include "../lib/NetPacks.h"
-#include "../lib/Connection.h"
-
-
-CBattleCallback::CBattleCallback(CGameState *GS, int Player, CClient *C )
-{
-	gs = GS;
-	player = Player;
-	cl = C;
-}
-
-bool CBattleCallback::battleMakeTacticAction( BattleAction * action )
-{
-	assert(cl->gs->curB->tacticDistance);
-	MakeAction ma;
-	ma.ba = *action;
-	sendRequest(&ma);
-	return true;
-}
-int CBattleCallback::battleMakeAction(BattleAction* action)
-{
-	assert(action->actionType == BattleAction::HERO_SPELL);
-	MakeCustomAction mca(*action);
-	sendRequest(&mca);
-	return 0;
-}
-
-void CBattleCallback::sendRequest(const CPack* request)
-{
-
-	//TODO should be part of CClient (client owns connection, not CB)
-	//but it would have to be very tricky cause template/serialization issues
-// 	if(waitTillRealize)
-// 		cl->waitingRequest.set(typeList.getTypeID(request));
-
-	cl->serv->sendPack(*request);
-
-// 	if(waitTillRealize)
-// 	{
-// 		if(unlockGsWhenWaiting)
-// 			getGsMutex().unlock_shared();
-// 		cl->waitingRequest.waitWhileTrue();
-// 		if(unlockGsWhenWaiting)
-// 			getGsMutex().lock_shared();
-// 	}
-}

+ 0 - 23
VCMI_BattleAiHost/BattleCallback.h

@@ -1,23 +0,0 @@
-#pragma once
-
-#include "../global.h"
-#include "../CCallback.h"
-
-/*class CBattleCallback : public IBattleCallback, public CBattleInfoCallback
-{
-private:
-	CBattleCallback(CGameState *GS, int Player, CClient *C);
-
-
-protected:
-	void sendRequest(const CPack *request);
-	CClient *cl;
-	//virtual bool hasAccess(int playerId) const;
-
-public:
-	int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack
-	bool battleMakeTacticAction(BattleAction * action) OVERRIDE; // performs tactic phase actions
-
-	friend class CCallback;
-	friend class CClient;
-};*/

+ 2 - 2
VCMI_BattleAiHost/Client.h

@@ -1,5 +1,6 @@
 #pragma once
 #include "../global.h"
+#include "../lib/CBattleCallback.h"
 class CGameState;
 class CConnection;
 struct CPack;
@@ -7,13 +8,12 @@ class CBattleGameInterface;
 struct BattleAction;
 class CStack;
 
-class CClient/* : public IGameCallback*/
+class CClient/* : public IGameCallback*/ : public IConnectionHandler
 {
 public:
 	bool terminate;
 	BattleAction *curbaction;
 	CGameState *gs;
-	CConnection *serv;
 	CBattleGameInterface *ai;
 
 	CClient();

+ 6 - 6
VCMI_BattleAiHost/NetPacksRunner.cpp

@@ -394,12 +394,12 @@ void EndAction::applyCl( CClient *cl )
 
 void PackageApplied::applyCl( CClient *cl )
 {
-// 	ui8 player = GS(cl)->currentPlayer;
-// 	INTERFACE_CALL_IF_PRESENT(player, requestRealized, this);
-// 	if(cl->waitingRequest.get() == packType)
-// 		cl->waitingRequest.setn(false);
-// 	else if(cl->waitingRequest.get())
-// 		tlog3 << "Surprising server message!\n";
+ 	ui8 player = GS(cl)->currentPlayer;
+ 	//INTERFACE_CALL_IF_PRESENT(player, requestRealized, this);
+ 	if(cl->waitingRequest.get() == packType)
+ 		cl->waitingRequest.setn(false);
+ 	else if(cl->waitingRequest.get())
+ 		tlog3 << "Surprising server message!\n";
 }
 
 void SystemMessage::applyCl( CClient *cl )

+ 0 - 2
VCMI_BattleAiHost/VCMI_BattleAiHost.vcxproj

@@ -132,13 +132,11 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <ClCompile Include="BattleCallback.cpp" />
     <ClCompile Include="Client.cpp" />
     <ClCompile Include="main.cpp" />
     <ClCompile Include="NetPacksRunner.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="BattleCallback.h" />
     <ClInclude Include="CheckTime.h" />
     <ClInclude Include="Client.h" />
   </ItemGroup>

+ 15 - 0
VCMI_VS10.sln

@@ -22,6 +22,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Odpalarka", "Odpalarka\Odpa
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
 	EndProjectSection
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C710950-7839-490C-8412-C60AC4D5238B}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VCMI_client", "client\VCMI_client.vcxproj", "{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -94,6 +98,17 @@ Global
 		{27B69211-D138-4142-BBC9-40F5DEA39067}.Release|Win32.ActiveCfg = Release|Win32
 		{27B69211-D138-4142-BBC9-40F5DEA39067}.Release|Win32.Build.0 = Release|Win32
 		{27B69211-D138-4142-BBC9-40F5DEA39067}.Release|x64.ActiveCfg = Release|Win32
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|Win32.Build.0 = Debug|Win32
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|x64.ActiveCfg = Debug|x64
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|x64.Build.0 = Debug|x64
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|Win32.ActiveCfg = RD|Win32
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|Win32.Build.0 = RD|Win32
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|x64.ActiveCfg = RD|x64
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|x64.Build.0 = RD|x64
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Release|Win32.ActiveCfg = RD|x64
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Release|x64.ActiveCfg = RD|x64
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Release|x64.Build.0 = RD|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 3 - 1
b1.json

@@ -6,7 +6,9 @@
 	[
 		{
 			"side" : 0,
-			"army" : [[10, 40]]
+			"army" : [[10, 40]],
+			"heroid" : 0,
+			"spells" : [1,2,3,4]
 		}
 		{
 			"side" : 1,

+ 5 - 2
client/CBattleInterface.cpp

@@ -1193,6 +1193,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	ObjectConstruction h__l__p(this);
 
 	if(!curInt) curInt = LOCPLINT; //may happen when we are defending during network MP game
+	bool duelMode = curInt->cb->getMyColor() < 0;
 
 	animsAreDisplayed.setn(false);
 	pos = myRect;
@@ -1297,8 +1298,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	bOptions = new AdventureMapButton (CGI->generaltexth->zelp[381].first, CGI->generaltexth->zelp[381].second, boost::bind(&CBattleInterface::bOptionsf,this), 3 + pos.x, 561 + pos.y, "icm003.def", SDLK_o);
 	bSurrender = new AdventureMapButton (CGI->generaltexth->zelp[379].first, CGI->generaltexth->zelp[379].second, boost::bind(&CBattleInterface::bSurrenderf,this), 54 + pos.x, 561 + pos.y, "icm001.def", SDLK_s);
 	bFlee = new AdventureMapButton (CGI->generaltexth->zelp[380].first, CGI->generaltexth->zelp[380].second, boost::bind(&CBattleInterface::bFleef,this), 105 + pos.x, 561 + pos.y, "icm002.def", SDLK_r);
-	bFlee->block(!curInt->cb->battleCanFlee());	
-	bSurrender->block(curInt->cb->battleGetSurrenderCost() < 0);
+	bFlee->block(duelMode || !curInt->cb->battleCanFlee());	
+	bSurrender->block(duelMode || curInt->cb->battleGetSurrenderCost() < 0);
 	bAutofight  = new AdventureMapButton (CGI->generaltexth->zelp[382].first, CGI->generaltexth->zelp[382].second, boost::bind(&CBattleInterface::bAutofightf,this), 157 + pos.x, 561 + pos.y, "icm004.def", SDLK_a);
 	bSpell = new AdventureMapButton (CGI->generaltexth->zelp[385].first, CGI->generaltexth->zelp[385].second, boost::bind(&CBattleInterface::bSpellf,this), 645 + pos.x, 561 + pos.y, "icm005.def", SDLK_c);
 	bSpell->block(true);
@@ -3869,6 +3870,8 @@ void CBattleInterface::endAction(const BattleAction* action)
 
 	if(tacticsMode) //we have activated next stack after sending request that has been just realized -> blockmap due to movement has changed
 		redrawBackgroundWithHexes(activeStack);
+
+	waitForAnims();
 }
 
 void CBattleInterface::hideQueue()

+ 3 - 0
client/CBattleInterface.h

@@ -456,6 +456,9 @@ private:
 	bool spellDestSelectMode; //if true, player is choosing destination for his spell
 	SpellSelectionType spellSelMode;
 	BattleAction * spellToCast; //spell for which player is choosing destination
+
+	bool duelMode;
+
 	void endCastingSpell(); //ends casting spell (eg. when spell has been cast or canceled)
 
 	void showAliveStack(const CStack *stack, SDL_Surface * to); //helper function for function show

+ 3 - 4
client/CMT.cpp

@@ -285,9 +285,6 @@ int main(int argc, char** argv)
 	{
 		StartInfo *si = new StartInfo();
 		si->mode = StartInfo::DUEL;
-		si->mapname = vm["battle"].as<std::string>();
-		si->playerInfos[0].color = 0;
-		si->playerInfos[1].color = 1;
 		startGame(si);
 	}
 	mainGUIThread = new boost::thread(&CGuiHandler::run, boost::ref(GH));
@@ -718,9 +715,11 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
 	{
 	case StartInfo::NEW_GAME:
 	case StartInfo::CAMPAIGN:
-	case StartInfo::DUEL:
 		client->newGame(serv, options);
 		break;
+	case StartInfo::DUEL:
+		client->newDuel(serv, options);
+		break;
 	case StartInfo::LOAD_GAME:
 		std::string fname = options->mapname;
 		boost::algorithm::erase_last(fname,".vlgm1");

+ 50 - 2
client/Client.cpp

@@ -101,13 +101,11 @@ void CClient::init()
 }
 
 CClient::CClient(void)
-:waitingRequest(0)
 {
 	init();
 }
 
 CClient::CClient(CConnection *con, StartInfo *si)
-:waitingRequest(0)
 {
 	init();
 	newGame(con,si);
@@ -418,6 +416,56 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 // 	}
 }
 
+void CClient::newDuel(CConnection *con, StartInfo *si)
+{
+	serv = con;
+	if(!serv)
+	{
+		std::string host = "127.0.0.1";
+		std::string port = "3030";
+
+		int i = 3;
+		while(!serv)
+		{
+			try
+			{
+				tlog0 << "Establishing connection...\n";
+				serv = new CConnection(host, port, "DLL host");
+			}
+			catch(...)
+			{
+				tlog1 << "\nCannot establish connection! Retrying within 2 seconds" << std::endl;
+				boost::this_thread::sleep(boost::posix_time::seconds(2));
+				if(!--i)
+					exit(0);
+			}
+		}
+	}
+
+
+	ui8 color;
+	std::string battleAIName;
+	*serv >> *si >> battleAIName >> color;
+	assert(si->mode == StartInfo::DUEL);
+	assert(color > 1); //we are NOT participants
+	//tlog0 << format("Server wants us to be %s in battle %s as side %d") % battleAIName % si.mapname % (int)color;
+
+	gs = new CGameState();
+	const_cast<CGameInfo*>(CGI)->state = gs;
+	//gs->scenarioOps = si;
+	gs->init(si, 0, 0);
+
+	CPlayerInterface *p = new CPlayerInterface(-1);
+	privilagedBattleEventReceivers.push_back(p);
+	p->observerInDuelMode = true;
+	battleints[254] = playerint[254] = p;
+	GH.curInt = p;
+	p->init(new CCallback(gs, -1, this));
+	battleStarted(gs->curB);
+
+	serv->addStdVecItems(const_cast<CGameInfo*>(CGI)->state);
+}
+
 template <typename Handler>
 void CClient::serialize( Handler &h, const int version )
 {

+ 3 - 5
client/Client.h

@@ -3,10 +3,9 @@
 
 
 #include "../global.h"
-#include <boost/thread.hpp>
 #include "../lib/IGameCallback.h"
-#include "../lib/CondSh.h"
 #include <queue>
+#include "../lib/CBattleCallback.h"
 
 /*
  * Client.h, part of VCMI engine
@@ -60,7 +59,7 @@ public:
 };
 
 /// Class which handles client - server logic
-class CClient : public IGameCallback
+class CClient : public IGameCallback, public IConnectionHandler
 {
 public:
 	CCallback *cb;
@@ -70,7 +69,6 @@ public:
 	std::map<ui8,CGameInterface *> playerint;
 	std::map<ui8,CBattleGameInterface *> battleints;
 	bool hotSeat;
-	CConnection *serv;
 	BattleAction *curbaction;
 
 	CPathsInfo *pathInfo;
@@ -78,7 +76,6 @@ public:
 
 	CScriptingModule *erm;
 
-	CondSh<int> waitingRequest;
 
 	std::queue<CPack *> packs;
 	boost::mutex packsM;
@@ -91,6 +88,7 @@ public:
 
 	void init();
 	void newGame(CConnection *con, StartInfo *si); //con - connection to server
+	void newDuel(CConnection *con, StartInfo *si); //con - connection to server
 
 	void loadNeutralBattleAI();
 	void endGame(bool closeConnection = true);

+ 61 - 0
lib/CBattleCallback.cpp

@@ -0,0 +1,61 @@
+#define VCMI_DLL
+#include "CBattleCallback.h"
+#include "NetPacks.h"
+#include "Connection.h"
+#include "CGameState.h"
+#include <boost/thread/shared_mutex.hpp>
+#include "BattleState.h"
+
+CBattleCallback::CBattleCallback(CGameState *GS, int Player, IConnectionHandler *C )
+{
+	gs = GS;
+	player = Player;
+	connHandler = C;
+}
+
+bool CBattleCallback::battleMakeTacticAction( BattleAction * action )
+{
+	assert(gs->curB->tacticDistance);
+	MakeAction ma;
+	ma.ba = *action;
+	sendRequest(&ma);
+	return true;
+}
+int CBattleCallback::battleMakeAction(BattleAction* action)
+{
+	assert(action->actionType == BattleAction::HERO_SPELL);
+	MakeCustomAction mca(*action);
+	sendRequest(&mca);
+	return 0;
+}
+
+void CBattleCallback::sendRequest(const CPack* request)
+{
+
+	//TODO should be part of CClient (client owns connection, not CB)
+	//but it would have to be very tricky cause template/serialization issues
+	if(waitTillRealize)
+	 	connHandler->waitingRequest.set(typeList.getTypeID(request));
+
+	connHandler->serv->sendPack(*request);
+
+	if(waitTillRealize)
+	{
+	 	if(unlockGsWhenWaiting)
+	 		gs->mx->unlock_shared();
+	 	connHandler->waitingRequest.waitWhileTrue();
+	 	if(unlockGsWhenWaiting)
+	 		gs->mx->lock_shared();
+	}
+}
+
+IConnectionHandler::IConnectionHandler()
+	: waitingRequest(0)
+{
+
+}
+
+IConnectionHandler::~IConnectionHandler()
+{
+
+}

+ 42 - 0
lib/CBattleCallback.h

@@ -0,0 +1,42 @@
+#pragma once
+#include "../global.h"
+#include "IGameCallback.h"
+#include "CondSh.h"
+
+class CConnection;
+
+class DLL_EXPORT IConnectionHandler
+{
+public:
+	IConnectionHandler();
+	~IConnectionHandler();
+
+	CConnection *serv;
+	CondSh<int> waitingRequest;
+};
+
+class DLL_EXPORT IBattleCallback
+{
+public:
+	bool waitTillRealize; //if true, request functions will return after they are realized by server
+	bool unlockGsWhenWaiting;//if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback!
+	//battle
+	virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
+	virtual bool battleMakeTacticAction(BattleAction * action) =0; // performs tactic phase actions
+};
+
+class DLL_EXPORT CBattleCallback : public IBattleCallback, public CBattleInfoCallback
+{
+protected:
+	void sendRequest(const CPack *request);
+	IConnectionHandler *connHandler;
+	//virtual bool hasAccess(int playerId) const;
+
+public:
+	CBattleCallback(CGameState *GS, int Player, IConnectionHandler *C);
+	int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack
+	bool battleMakeTacticAction(BattleAction * action) OVERRIDE; // performs tactic phase actions
+
+	friend class CCallback;
+	friend class CClient;
+};

+ 6 - 0
lib/CGameState.cpp

@@ -2720,6 +2720,12 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname)
 			ss.stacks[i].count = stackNode.Vector()[1].Float();
 			i++;
 		}
+
+		ss.heroId = n["heroid"].Float();
+		if(ss.heroId != -1)
+			BOOST_FOREACH(const JsonNode &spell, n["spells"].Vector())
+				ss.spells.insert(spell.Float());
+		
 	}
 
 	return ret;

+ 2 - 0
lib/VCMI_lib.vcxproj

@@ -214,6 +214,7 @@
     <ClCompile Include="BattleAction.cpp" />
     <ClCompile Include="BattleState.cpp" />
     <ClCompile Include="CArtHandler.cpp" />
+    <ClCompile Include="CBattleCallback.cpp" />
     <ClCompile Include="CBuildingHandler.cpp" />
     <ClCompile Include="CCampaignHandler.cpp" />
     <ClCompile Include="CCreatureHandler.cpp" />
@@ -247,6 +248,7 @@
     <ClInclude Include="..\CConsoleHandler.h" />
     <ClInclude Include="BattleState.h" />
     <ClInclude Include="CArtHandler.h" />
+    <ClInclude Include="CBattleCallback.h" />
     <ClInclude Include="CBuildingHandler.h" />
     <ClInclude Include="CCampaignHandler.h" />
     <ClInclude Include="CCreatureHandler.h" />

+ 6 - 3
server/CGameHandler.cpp

@@ -456,7 +456,6 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 		winnerHero->showNecromancyDialog(raisedStack);
 		addToSlot(StackLocation(winnerHero, necroSlot), raisedStack.type, raisedStack.count);
 	}
-	sendAndApply(&resultsApplied);
 
 	if(duel)
 	{
@@ -471,10 +470,14 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 			tlog0 << boost::format("\t* %d of %s\n") % i->second % c->namePl;
 			casualtiesPoints = c->AIValue * i->second;
 		}
-		tlog0 << boost::format("Total causalties points: %d\n") % casualtiesPoints;
-		return;
+		tlog0 << boost::format("Total casualties points: %d\n") % casualtiesPoints;
 	}
 
+	sendAndApply(&resultsApplied);
+
+	if(duel)
+		return;
+
 	if(visitObjectAfterVictory && winnerHero == hero1)
 	{
 		visitObjectOnTile(*getTile(winnerHero->getPosition()), winnerHero);