浏览代码

Some very early work towards autofight feature.
Added EmptyAI to the solution.
Passing callbacks by shared_ptr.

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

+ 2 - 2
AI/BattleAI/BattleAI.cpp

@@ -8,7 +8,7 @@
 #include "../../lib/VCMI_Lib.h"
 
 using boost::optional;
-CBattleCallback * cbc;
+shared_ptr<CBattleCallback> cbc;
 
 #define LOGL(text) print(text)
 #define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
@@ -85,7 +85,7 @@ CBattleAI::~CBattleAI(void)
 	}
 }
 
-void CBattleAI::init( CBattleCallback * CB )
+void CBattleAI::init(shared_ptr<CBattleCallback> CB)
 {
 	print("init called, saving ptr to IBattleCallback");
 	cbc = cb = CB;

+ 2 - 2
AI/BattleAI/BattleAI.h

@@ -109,7 +109,7 @@ struct PotentialTargets
 class CBattleAI : public CBattleGameInterface
 {
 	int side;
-	CBattleCallback *cb;
+	shared_ptr<CBattleCallback> cb;
 	
 	//Previous setting of cb 
 	bool wasWaitingForRealize, wasUnlockingGs;
@@ -119,7 +119,7 @@ public:
 	CBattleAI(void);
 	~CBattleAI(void);
 
-	void init(CBattleCallback * CB) OVERRIDE;
+	void init(shared_ptr<CBattleCallback> CB) OVERRIDE;
 	void actionFinished(const BattleAction &action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
 	void actionStarted(const BattleAction &action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
 	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack

+ 1 - 1
AI/EmptyAI/CEmptyAI.cpp

@@ -1,7 +1,7 @@
 #include "StdInc.h"
 #include "CEmptyAI.h"
 
-void CEmptyAI::init(CCallback * CB)
+void CEmptyAI::init(shared_ptr<CCallback> CB)
 {
 	cb = CB;
 	human=false;

+ 2 - 2
AI/EmptyAI/CEmptyAI.h

@@ -7,10 +7,10 @@ struct HeroMoveDetails;
 
 class CEmptyAI : public CGlobalAI
 {
-	CCallback *cb;
+	shared_ptr<CCallback> cb;
 
 public:
-	void init(CCallback * CB) override;
+	void init(shared_ptr<CCallback> CB) override;
 	void yourTurn() override;
 	void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
 	void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;

+ 9 - 4
AI/EmptyAI/EmptyAI.vcxproj

@@ -41,23 +41,27 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v110_xp</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v110_xp</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -106,7 +110,8 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <PreprocessorDefinitions>VCMI_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalOptions>/Zm130 %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
@@ -122,7 +127,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <PreprocessorDefinitions>VCMI_DLL;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
@@ -140,7 +145,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <PreprocessorDefinitions>VCMI_DLL;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
@@ -160,7 +165,7 @@
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <PreprocessorDefinitions>VCMI_DLL;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>

+ 2 - 2
AI/StupidAI/StupidAI.cpp

@@ -5,7 +5,7 @@
 #include "../../CCallback.h"
 #include "../../lib/CCreatureHandler.h"
 
-CPlayerBattleCallback * cbc;
+shared_ptr<CBattleCallback> cbc;
 
 CStupidAI::CStupidAI(void)
 	: side(-1), cb(NULL)
@@ -19,7 +19,7 @@ CStupidAI::~CStupidAI(void)
 	print("destroyed");
 }
 
-void CStupidAI::init( CBattleCallback * CB )
+void CStupidAI::init(shared_ptr<CBattleCallback> CB)
 {
 	print("init called, saving ptr to IBattleCallback");
 	cbc = cb = CB;

+ 2 - 2
AI/StupidAI/StupidAI.h

@@ -5,14 +5,14 @@
 class CStupidAI : public CBattleGameInterface
 {
 	int side;
-	CBattleCallback *cb;
+	shared_ptr<CBattleCallback> cb;
 
 	void print(const std::string &text) const;
 public:
 	CStupidAI(void);
 	~CStupidAI(void);
 
-	void init(CBattleCallback * CB) OVERRIDE;
+	void init(shared_ptr<CBattleCallback> CB) OVERRIDE;
 	void actionFinished(const BattleAction &action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
 	void actionStarted(const BattleAction &action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
 	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack

+ 2 - 2
AI/VCAI/VCAI.cpp

@@ -34,7 +34,7 @@ struct SetGlobalState
 		assert(!cb.get());
 
 		ai.reset(AI);
-		cb.reset(AI->myCb);
+		cb.reset(AI->myCb.get());
 	}
 	~SetGlobalState()
 	{
@@ -823,7 +823,7 @@ void VCAI::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor
 	NET_EVENT_HANDLER;
 }
 
-void VCAI::init(CCallback * CB)
+void VCAI::init(shared_ptr<CCallback> CB)
 {
 	LOG_TRACE(logAi);
 	myCb = CB;

+ 2 - 2
AI/VCAI/VCAI.h

@@ -291,7 +291,7 @@ public:
 	AIStatus status;
 	std::string battlename;
 
-	CCallback *myCb;
+	shared_ptr<CCallback> myCb;
 
 	unique_ptr<boost::thread> makingTurn;
 
@@ -306,7 +306,7 @@ public:
 
 	virtual std::string getBattleAIName() const OVERRIDE;
 
-	virtual void init(CCallback * CB) OVERRIDE;
+	virtual void init(shared_ptr<CCallback> CB) OVERRIDE;
 	virtual void yourTurn() OVERRIDE;
 
 	virtual void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) OVERRIDE; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id

+ 17 - 0
VCMI_VS11.sln

@@ -35,6 +35,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Editor", "editor\Editor.vcx
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EmptyAI", "AI\EmptyAI\EmptyAI.vcxproj", "{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}"
+	ProjectSection(ProjectDependencies) = postProject
+		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -146,6 +151,18 @@ Global
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.ActiveCfg = Release|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.Build.0 = Release|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|Win32.ActiveCfg = Debug|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|Win32.Build.0 = Debug|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|x64.ActiveCfg = Debug|x64
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|x64.Build.0 = Debug|x64
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|Win32.ActiveCfg = RD|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|Win32.Build.0 = RD|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|x64.ActiveCfg = RD|x64
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|x64.Build.0 = RD|x64
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Release|Win32.ActiveCfg = RD|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Release|Win32.Build.0 = RD|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Release|x64.ActiveCfg = RD|x64
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Release|x64.Build.0 = RD|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 2 - 2
client/CPlayerInterface.cpp

@@ -135,9 +135,9 @@ CPlayerInterface::~CPlayerInterface()
 
 	LOCPLINT = NULL;
 }
-void CPlayerInterface::init(CCallback * CB)
+void CPlayerInterface::init(shared_ptr<CCallback> CB)
 {
-	cb = dynamic_cast<CCallback*>(CB);
+	cb = CB;
 	if(observerInDuelMode)
 		return;
 

+ 2 - 2
client/CPlayerInterface.h

@@ -101,7 +101,7 @@ public:
 	static CBattleInterface * battleInt; //NULL if no battle
 	CInGameConsole * cingconsole;
 
-	CCallback * cb; //to communicate with engine
+	shared_ptr<CCallback> cb; //to communicate with engine
 	const BattleAction *curAction; //during the battle - action currently performed by active stack (or NULL)
 
 	std::list<CInfoWindow *> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
@@ -221,7 +221,7 @@ public:
 	void openTownWindow(const CGTownInstance * town); //shows townscreen
 	void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
 	void updateInfo(const CGObjectInstance * specific);
-	void init(CCallback * CB);
+	void init(shared_ptr<CCallback> CB);
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
 	void showInfoDialog(const std::string &text, CComponent * component);
 	void showInfoDialog(const std::string &text, const std::vector<CComponent*> & components = std::vector<CComponent*>(), int soundID = 0, bool delComps = false);

+ 70 - 47
client/Client.cpp

@@ -91,7 +91,6 @@ void CClient::init()
 	IObjectInterface::cb = this;
 	serv = nullptr;
 	gs = nullptr;
-	cb = nullptr;
 	erm = nullptr;
 	terminate = false;
 }
@@ -216,6 +215,7 @@ void CClient::endGame( bool closeConnection /*= true*/ )
 	}
 
 	callbacks.clear();
+	battleCallbacks.clear();
     logNetwork->infoStream() << "Deleted playerInts.";
 
     logNetwork->infoStream() << "Client stopped.";
@@ -372,7 +372,6 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	}
 
 	int humanPlayers = 0;
-	int sensibleAILimit = settings["session"]["oneGoodAI"].Bool() ? 1 : PlayerColor::PLAYER_LIMIT_I;
 	for(auto it = gs->scenarioOps->playerInfos.begin(); 
 		it != gs->scenarioOps->playerInfos.end(); ++it)//initializing interfaces for players
 	{
@@ -384,45 +383,22 @@ void CClient::newGame( CConnection *con, StartInfo *si )
         logNetwork->traceStream() << "Preparing interface for player " << color;
 		if(si->mode != StartInfo::DUEL)
 		{
-			auto cb = make_shared<CCallback>(gs,color,this);
 			if(it->second.playerID == PlayerSettings::PLAYER_AI)
 			{
-				std::string AItoGive = settings["server"]["playerAI"].String();
-				if(!sensibleAILimit)
-					AItoGive = "EmptyAI";
-				else
-					sensibleAILimit--;
-				playerint[color] = static_cast<CGameInterface*>(CDynLibHandler::getNewAI(AItoGive));
-                logNetwork->infoStream() << "Player " << static_cast<int>(color.getNum()) << " will be lead by " << AItoGive;
+				auto AiToGive = aiNameForPlayer(it->second, false);
+				logNetwork->infoStream() << boost::format("Player %s will be lead by %s") % color % AiToGive;
+				installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color);
 			}
 			else 
 			{
-				playerint[color] = new CPlayerInterface(color);
+				installNewPlayerInterface(new CPlayerInterface(color), color);
 				humanPlayers++;
 			}
-			battleints[color] = playerint[color];
-
-            logNetwork->traceStream() << "\tInitializing the interface";
-			playerint[color]->init(cb.get());
-			battleCallbacks[color] = callbacks[color] = cb;
 		}
 		else
 		{
-			auto cbc = make_shared<CBattleCallback>(gs, color, this);
-			battleCallbacks[color] = cbc;
-
-			std::string AItoGive = it->second.name;
-			if(AItoGive.empty())
-			{
-				if(color == PlayerColor(0))
-					battleints[color] = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
-				else
-					battleints[color] = CDynLibHandler::getNewBattleAI("StupidAI");
-			}
-
-
-			battleints[color] = CDynLibHandler::getNewBattleAI(AItoGive);
-			battleints[color]->init(cbc.get());
+			std::string AItoGive = aiNameForPlayer(it->second, true);
+			installNewBattleInterface(CDynLibHandler::getNewBattleAI(AItoGive), color);
 		}
 	}
 
@@ -433,12 +409,8 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 			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);
+			installNewPlayerInterface(p, boost::none);
 			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);
 	}
@@ -502,10 +474,7 @@ void CClient::serialize( Handler &h, const int version )
 			{
 				if(pid == PlayerColor::NEUTRAL)
 				{
-					//CBattleCallback * cbc = new CBattleCallback(gs, pid, this);//FIXME: unused?
-					CBattleGameInterface *cbgi = CDynLibHandler::getNewBattleAI(dllname);
-					battleints[pid] = cbgi;
-					cbgi->init(cb);
+					installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
 					//TODO? consider serialization 
 					continue;
 				}
@@ -525,9 +494,7 @@ void CClient::serialize( Handler &h, const int version )
 			nInt->human = isHuman;
 			nInt->playerID = pid;
 
-			battleCallbacks[pid] = callbacks[pid] = make_shared<CCallback>(gs,pid,this);
-			battleints[pid] = playerint[pid] = nInt;
-			nInt->init(callbacks[pid].get());
+			installNewPlayerInterface(nInt, pid);
 			nInt->loadGame(dynamic_cast<CISer<CLoadFile>&>(h), version); //another evil cast, check above
 		}
 
@@ -658,10 +625,7 @@ void CClient::battleFinished()
 
 void CClient::loadNeutralBattleAI()
 {
-	battleints[PlayerColor::NEUTRAL] = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
-	auto cbc = make_shared<CBattleCallback>(gs, PlayerColor::NEUTRAL, this);
-	battleCallbacks[PlayerColor::NEUTRAL] = cbc;
-	battleints[PlayerColor::NEUTRAL]->init(cbc.get());
+	installNewBattleInterface(CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String()), PlayerColor::NEUTRAL);
 }
 
 void CClient::commitPackage( CPackForClient *pack )
@@ -746,6 +710,65 @@ void CClient::campaignMapFinished( shared_ptr<CCampaignState> camp )
 	}
 }
 
+void CClient::installNewPlayerInterface(CGameInterface *gameInterface, boost::optional<PlayerColor> color)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
+	PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
+
+	if(!color) 
+		privilagedGameEventReceivers.push_back(gameInterface);
+
+	playerint[colorUsed] = gameInterface;
+
+	logGlobal->traceStream() << boost::format("\tInitializing the interface for player %s") % colorUsed;
+	auto cb = make_shared<CCallback>(gs, color, this);
+	callbacks[colorUsed] = cb;
+	battleCallbacks[colorUsed] = cb;
+	gameInterface->init(cb);
+
+	installNewBattleInterface(gameInterface, color, false);
+}
+
+void CClient::installNewBattleInterface(CBattleGameInterface* battleInterface, boost::optional<PlayerColor> color, bool needCallback /*= true*/)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
+	PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
+
+	if(!color) 
+		privilagedBattleEventReceivers.push_back(battleInterface);
+
+	battleints[colorUsed] = battleInterface;
+
+	if(needCallback)
+	{
+		logGlobal->traceStream() << boost::format("\tInitializing the battle interface for player %s") % *color;
+		auto cbc = make_shared<CBattleCallback>(gs, color, this);
+		battleCallbacks[colorUsed] = cbc;
+		battleInterface->init(cbc);
+	}
+}
+
+std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
+{
+	if(ps.name.size())
+	{
+		std::string filename = VCMIDirs::get().libraryPath() + "/AI/" + VCMIDirs::get().libraryName(ps.name);
+		if(boost::filesystem::exists(filename))
+			return ps.name;
+	}
+
+	const int sensibleAILimit = settings["session"]["oneGoodAI"].Bool() ? 1 : PlayerColor::PLAYER_LIMIT_I;
+	std::string goodAI = battleAI ? settings["server"]["neutralAI"].String() : settings["server"]["playerAI"].String();
+	std::string badAI = battleAI ? "StupidAI" : "EmptyAI";
+
+
+	//TODO what about human players
+	if(battleints.size() >= sensibleAILimit)
+		return badAI;
+
+	return goodAI;
+}
+
 template void CClient::serialize( CISer<CLoadFile> &h, const int version );
 template void CClient::serialize( COSer<CSaveFile> &h, const int version );
 

+ 4 - 1
client/Client.h

@@ -112,7 +112,6 @@ public:
 class CClient : public IGameCallback
 {
 public:
-	CCallback *cb;
 	std::map<PlayerColor,shared_ptr<CCallback> > callbacks; //callbacks given to player interfaces
 	std::map<PlayerColor,shared_ptr<CBattleCallback> > battleCallbacks; //callbacks given to player interfaces
 	std::vector<IGameEventsReceiver*> privilagedGameEventReceivers; //scripting modules, spectator interfaces
@@ -141,6 +140,10 @@ public:
 	void newGame(CConnection *con, StartInfo *si); //con - connection to server
 
 	void loadNeutralBattleAI();
+	void installNewPlayerInterface(CGameInterface *gameInterface, boost::optional<PlayerColor> color);
+	void installNewBattleInterface(CBattleGameInterface* battleInterface, boost::optional<PlayerColor> color, bool needCallback = true);
+	std::string aiNameForPlayer(const PlayerSettings &ps, bool battleAI); //empty means no AI -> human
+
 	void endGame(bool closeConnection = true);
 	void stopConnection();
 	void save(const std::string & fname);

+ 47 - 4
client/battle/CBattleInterface.cpp

@@ -93,8 +93,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	  activeStack(NULL), stackToActivate(NULL), selectedStack(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1),
 	  currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL),  stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(NULL), sp(NULL),
 	  siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0),
-	  givenCommand(NULL), myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL)
-
+	  givenCommand(NULL), myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL),
+	  autofightingAI(nullptr), background(nullptr)
 {
 	OBJ_CONSTRUCTION;
 
@@ -172,8 +172,16 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	}
 	else
 	{
-		std::vector< std::string > & backref = graphics->battleBacks[ curInt->cb->battleGetBattlefieldType() ];
-		background = BitmapHandler::loadBitmap(backref[ rand() % backref.size()], false );
+		auto bfieldType = (int)curInt->cb->battleGetBattlefieldType();
+		if(graphics->battleBacks.size() <= bfieldType || bfieldType < 0)
+			logGlobal->errorStream() << bfieldType << " is not valid battlefield type index!";
+		else if(graphics->battleBacks[bfieldType].empty())
+			logGlobal->errorStream() << bfieldType << " battlefield type does not have any backgrounds!";
+		else
+		{
+			const std::string bgName = vstd::pickRandomElementOf(graphics->battleBacks[bfieldType], rand);
+			background = BitmapHandler::loadBitmap(bgName, false);
+		}
 	}
 
 	//preparing menu background
@@ -446,6 +454,8 @@ CBattleInterface::~CBattleInterface()
 
 	delete siegeH;
 
+	delete autofightingAI;
+
 	//TODO: play AI tracks if battle was during AI turn
 	//if (!curInt->makingTurn)
 	//CCS->musich->playMusicFromSet(CCS->musich->aiMusics, -1);
@@ -1254,6 +1264,39 @@ void CBattleInterface::bAutofightf()
 {
 	if(spellDestSelectMode) //we are casting a spell
 		return;
+	
+	static bool isAutoFightOn = false;
+	static unique_ptr<boost::thread> aiThread = nullptr;
+
+	if(isAutoFightOn)
+	{
+		assert(autofightingAI);
+		isAutoFightOn = false;
+		aiThread->join();
+
+		vstd::clear_pointer(autofightingAI);
+		aiThread = nullptr;
+	}
+	else
+	{
+		isAutoFightOn = true;
+		autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
+		autofightingAI->init(curInt->cb);
+		autofightingAI->battleStart(army1, army2, int3(0,0,0), attackingHeroInstance, defendingHeroInstance, curInt->cb->battleGetMySide());
+
+		//Deactivate everything
+		deactivate();
+		bAutofight->activate(); //only autofight button is to remain active
+		aiThread = make_unique<boost::thread>([&] 
+		{
+			auto ba = new BattleAction(autofightingAI->activeStack(activeStack));
+
+			if(isAutoFightOn)
+			{
+				givenCommand->setn(ba);
+			}
+		});
+	}
 }
 
 void CBattleInterface::bSpellf()

+ 3 - 0
client/battle/CBattleInterface.h

@@ -48,6 +48,7 @@ class CClickableHex;
 struct BattleHex;
 struct InfoAboutHero;
 struct BattleAction;
+class CBattleGameInterface;
 
 /// Class which manages the locked hex fields that are blocked e.g. by obstacles
 class CBattleObstacle
@@ -155,6 +156,8 @@ private:
 	PossibleActions selectedAction; //last action chosen (and saved) by player
 	PossibleActions illegalAction; //most likely action that can't be performed here
 
+	CBattleGameInterface *autofightingAI;
+
 	void getPossibleActionsForStack (const CStack * stack); //called when stack gets its turn
 	void endCastingSpell(); //ends casting spell (eg. when spell has been cast or canceled)
 

+ 3 - 3
lib/CGameInterface.h

@@ -61,7 +61,7 @@ public:
 	std::string dllName;
 
 	virtual ~CBattleGameInterface() {};
-	virtual void init(CBattleCallback * CB){};
+	virtual void init(shared_ptr<CBattleCallback> CB){};
 
 	//battle call-ins
 	virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack
@@ -76,7 +76,7 @@ public:
 class CGameInterface : public CBattleGameInterface, public IGameEventsReceiver
 {
 public:
-	virtual void init(CCallback * CB){};
+	virtual void init(shared_ptr<CCallback> CB){};
 	virtual void yourTurn(){}; //called AFTER playerStartsTurn(player)
 
 	//pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
@@ -115,7 +115,7 @@ public:
 	CAdventureAI() : battleAI(NULL), cbc(NULL) {};
 
 	CBattleGameInterface *battleAI;
-	CBattleCallback *cbc;
+	shared_ptr<CBattleCallback> cbc;
 
 	virtual std::string getBattleAIName() const = 0; //has to return name of the battle AI to be used
 

+ 3 - 0
server/CGameHandler.cpp

@@ -4384,6 +4384,9 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 	{
 	case Battle::HERO_SPELL:
 		{
+			COMPLAIN_RET_FALSE_IF(ba.side > 1, "Side must be 0 or 1!");
+				
+
 			const CGHeroInstance *h = gs->curB->heroes[ba.side];
 			const CGHeroInstance *secondHero = gs->curB->heroes[!ba.side];
 			if(!h)