Bläddra i källkod

[experiments2] It might be a good idea to have an automatization framework… or not so good.

Michał W. Urbańczyk 12 år sedan
förälder
incheckning
d6f4a3a4fa

+ 3 - 0
AI/BattleAI/BattleAI.vcxproj

@@ -144,6 +144,9 @@
     <ClInclude Include="BattleAI.h" />
     <ClInclude Include="..\..\Global.h" />
   </ItemGroup>
+  <ItemGroup>
+    <None Include="ClassDiagram.cd" />
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>

+ 216 - 0
AI/VisitObjects/ObjectVisitingModule.cpp

@@ -0,0 +1,216 @@
+#include "StdInc.h"
+#include "ObjectVisitingModule.h"
+#include "..\..\CCallback.h"
+#include "..\..\lib\CObjectHandler.h"
+#include "..\..\lib\CGameState.h"
+
+
+ObjectVisitingModule::ObjectVisitingModule(void)
+{
+}
+
+
+ObjectVisitingModule::~ObjectVisitingModule(void)
+{
+}
+
+void ObjectVisitingModule::receivedMessage(const boost::any &msg)
+{
+
+	if(auto txt = boost::any_cast<std::string>(&msg))
+	{
+		std::istringstream hlp(*txt);
+		std::string w1, w2;
+		hlp >> w1 >> w2;
+		if(w1 != "visit")
+			return;
+
+		if(w2 == "all")
+		{
+			destinations = getDestinations();
+			logGlobal->debugStream() << "Added as destination all possible objects:";
+		}
+		if(w2 == "list")
+		{
+			auto dsts = getDestinations();
+			logGlobal->debugStream() << "Possible visit destinations:";
+		}
+		if(w2 == "add")
+		{
+			auto dsts = getDestinations();
+
+			int no;
+			hlp >> no;
+			if(no < 0 || no >= dsts.size() || !hlp)
+			{
+				logGlobal->warnStream() << "Invalid number!";
+			}
+			else if(vstd::contains(destinations, dsts[no]))
+			{
+				logGlobal->warnStream() << "Object already present on destinations list!";
+			}
+			else
+			{
+				destinations.push_back(dsts[no]);
+			}
+		}
+	}
+}
+
+bool compareNodes(const CGPathNode *lhs, const CGPathNode *rhs)
+{
+	if(lhs->turns != rhs->turns)
+		return lhs->turns < rhs->turns;
+
+	return lhs->moveRemains > rhs->moveRemains;
+}
+
+void ObjectVisitingModule::executeInternal()
+{
+	int weekNow = cb->getDate(Date::WEEK);
+	if(weekNow != week)
+	{
+		visitedThisWeek.clear();
+		week = weekNow;
+	}
+
+	if(auto h = myHero())
+	{
+		cb->setSelection(h);
+
+		auto leftToVisit = destinations;
+
+		while(true)
+		{
+			vstd::erase_if(destinations, [this](const CGObjectInstance *obj) { 
+				return vstd::contains(visitedThisWeek, obj); });
+
+			if(destinations.empty())
+				return;
+
+			auto isCloser = [this] (const CGObjectInstance *lhs, const CGObjectInstance *rhs) -> bool
+			{
+				return compareNodes(cb->getPathInfo(lhs->visitablePos()), cb->getPathInfo(lhs->visitablePos()));
+			};
+
+			const auto toVisit = *range::min_element(leftToVisit, isCloser);
+			const int3 moveDest = (toVisit)->visitablePos();
+
+			CGPath path;
+			cb->getPath2(moveDest, path);
+
+			try
+			{
+				bool ret = moveHero(h, moveDest);
+			}
+			catch(...)
+			{
+				logAi->errorStream() <<boost::format("Something wrong with %s at %s, removing") % toVisit->getHoverText() % toVisit->pos;
+				destinations -= toVisit;
+			}
+		}
+	}
+}
+
+void ObjectVisitingModule::heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visitedObj, bool start)
+{
+	if(vstd::contains(destinations, visitedObj))
+		visitedThisWeek.insert(visitedObj);
+}
+
+bool ObjectVisitingModule::isInterestingObject(const CGObjectInstance *obj) const
+{
+	switch(obj->ID)
+	{
+	case Obj::WINDMILL:
+	case Obj::WATER_WHEEL:
+	case Obj::MYSTICAL_GARDEN:
+		return true;
+	default:
+		return false;
+	}
+}
+
+std::vector<const CGObjectInstance *> ObjectVisitingModule::getDestinations() const
+{
+	std::vector<const CGObjectInstance *> ret;
+
+	auto foreach_tile_pos = [this](boost::function<void(const int3& pos)> foo)
+	{
+		for(int i = 0; i < cb->getMapSize().x; i++)
+			for(int j = 0; j < cb->getMapSize().y; j++)
+				for(int k = 0; k < cb->getMapSize().z; k++)
+					foo(int3(i,j,k));
+	};
+
+
+	foreach_tile_pos([&](const int3 &pos)
+	{
+		BOOST_FOREACH(const CGObjectInstance *obj, cb->getVisitableObjs(pos, false))
+		{
+			if(isInterestingObject(obj))
+				ret.push_back(obj);
+		}
+	});
+
+	return ret;
+}
+
+void ObjectVisitingModule::printObjects(const std::vector<const CGObjectInstance *> &objs) const
+{
+	logGlobal->debugStream() << objs.size() << " objects:";
+	for(int i =  0; i < objs.size(); i++)
+		logGlobal->debugStream() << boost::format("\t%d. %s at %s.") % i % objs[i]->getHoverText() % objs[i]->pos;
+}
+
+const CGHeroInstance * ObjectVisitingModule::myHero() const
+{
+	auto heroes = getMyHeroes();
+	if(heroes.empty())
+		return nullptr;
+	return heroes.front();
+}
+
+bool ObjectVisitingModule::moveHero(const CGHeroInstance *h, int3 dst)
+{
+	int3 startHpos = h->visitablePos();
+	bool ret = false;
+	if(startHpos == dst)
+	{
+		assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
+		cb->moveHero(h, CGHeroInstance::convertPosition(dst, true));
+		ret = true;
+	}
+	else
+	{
+		CGPath path;
+		cb->getPath2(dst, path);
+		if(path.nodes.empty())
+		{
+			logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
+			cb->recalculatePaths();
+			throw std::runtime_error("Wrong move order!");
+		}
+
+		int i=path.nodes.size()-1;
+		for(; i>0; i--)
+		{
+			//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
+			if(path.nodes[i-1].turns)
+			{
+				//blockedHeroes.insert(h); //to avoid attempts of moving heroes with very little MPs
+				break;
+			}
+
+			int3 endpos = path.nodes[i-1].coord;
+			if(endpos == h->visitablePos())
+					continue;
+
+			logAi->debugStream() << "Moving " << h->name << " from " << h->getPosition() << " to " << endpos;
+			cb->moveHero(h, CGHeroInstance::convertPosition(endpos, true));
+		}
+		ret = !i;
+	}
+
+	return ret;
+}

+ 26 - 0
AI/VisitObjects/ObjectVisitingModule.h

@@ -0,0 +1,26 @@
+#pragma once
+#include "..\..\lib\CGameInterface.h"
+
+
+class ObjectVisitingModule : public CAutomationModule
+{
+	std::vector<const CGObjectInstance *> destinations;
+	std::set<const CGObjectInstance *> visitedThisWeek;
+	int week;
+
+public:
+	ObjectVisitingModule(void);
+	~ObjectVisitingModule(void);
+
+	virtual void receivedMessage(const boost::any &msg);
+	virtual void executeInternal();
+	virtual void heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visitedObj, bool start);
+
+	bool isInterestingObject(const CGObjectInstance *obj) const;
+	std::vector<const CGObjectInstance *> getDestinations() const;
+	void printObjects(const std::vector<const CGObjectInstance *> &objs)const;
+	const CGHeroInstance *myHero() const;
+
+	bool moveHero(const CGHeroInstance *hero, int3 dst);
+};
+

+ 2 - 0
AI/VisitObjects/StdInc.cpp

@@ -0,0 +1,2 @@
+// Creates the precompiled header
+#include "StdInc.h"

+ 7 - 0
AI/VisitObjects/StdInc.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#include "../../Global.h"
+
+// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
+
+// Here you can add specific libraries and macros which are specific to this project.

+ 149 - 0
AI/VisitObjects/VisitObjects.vcxproj

@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="RD|Win32">
+      <Configuration>RD</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="RD|x64">
+      <Configuration>RD</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{D3126B35-650E-46C8-A336-1D6640FBFFD0}</ProjectGuid>
+    <RootNamespace>BattleAI</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <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">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\VCMI_global_debug.props" />
+    <Import Project="..\..\VCMI_global.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\VCMI_global_debug.props" />
+    <Import Project="..\..\VCMI_global.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\VCMI_global_release.props" />
+    <Import Project="..\..\VCMI_global.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\VCMI_global_release.props" />
+    <Import Project="..\..\VCMI_global.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(VCMI_Out)\AI\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <OutDir>$(VCMI_Out)\AI\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <OutDir>$(VCMI_Out)\AI\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
+    <OutDir>$(VCMI_Out)\AI\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/Zm159 %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/Zm159 %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/Zm159 %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/Zm159 %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="main.cpp" />
+    <ClCompile Include="ObjectVisitingModule.cpp" />
+    <ClCompile Include="StdInc.cpp">
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">StdInc.h</PrecompiledHeaderFile>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="ObjectVisitingModule.h" />
+    <ClInclude Include="StdInc.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 25 - 0
AI/VisitObjects/main.cpp

@@ -0,0 +1,25 @@
+#include "StdInc.h"
+
+#include "../../lib/AI_Base.h"
+#include "ObjectVisitingModule.h"
+
+#ifdef __GNUC__
+#define strcpy_s(a, b, c) strncpy(a, c, b)
+#endif
+
+const char *g_cszAiName = "ObjectVisitingModule";
+
+extern "C" DLL_EXPORT int GetGlobalAiVersion()
+{
+	return AI_INTERFACE_VER;
+}
+
+extern "C" DLL_EXPORT void GetAiName(char* name)
+{
+	strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
+}
+
+extern "C" DLL_EXPORT void GetNewAutomationModule(shared_ptr<ObjectVisitingModule> &out)
+{
+	out = make_shared<ObjectVisitingModule>();
+}

+ 168 - 100
CCallback.cpp

@@ -396,138 +396,206 @@ void CCallback::unregisterBattleInterface(shared_ptr<IBattleEventsReceiver> batt
 	cl->additionalBattleInts[*player] -= battleEvents;
 }
 
-CBattleCallback::CBattleCallback(CGameState *GS, boost::optional<PlayerColor> 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;
-}
-
-bool CAutomationCallback::moveHero(const CGHeroInstance *h, int3 dst)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-bool CAutomationCallback::dismissHero(const CGHeroInstance * hero)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-void CAutomationCallback::dig(const CGObjectInstance *hero)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-void CAutomationCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos /*= int3(-1, -1, -1) */)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-void CAutomationCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)
+shared_ptr<CAutomationModule> CCallback::createAutomationModule(const std::string &moduleName, const ObjectsCeded &objects, const boost::any &initialMessage /*= boost::none*/)
 {
-	throw std::exception("The method or operation is not implemented.");
+	auto module = CDynLibHandler::getNewAutomationModule(moduleName);
+	installAutomationModule(module, objects);
+	module->receivedMessage(initialMessage);
+	return module;
 }
 
-bool CAutomationCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
+void CCallback::installAutomationModule(shared_ptr<CAutomationModule> module, const ObjectsCeded &objects)
 {
-	throw std::exception("The method or operation is not implemented.");
+	auto cb = make_shared<CAutomationCallback>(gs, player, cl, module);
+	module->automationInit(cb, objects);
+	registerGameInterface(module);
+	cb->waitTillRealize = module->needsWaitingForRealize();
+	cb->unlockGsWhenWaiting = module->needsUnlockingGs();
+	module->modulePrepared();
 }
 
-void CAutomationCallback::recruitCreatures(const CGObjectInstance *obj, CreatureID ID, ui32 amount, si32 level/*=-1*/)
+void CCallback::removeAutomationModule(shared_ptr<CAutomationModule> module)
 {
-	throw std::exception("The method or operation is not implemented.");
+	unregisterGameInterface(module);
 }
 
-bool CAutomationCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID/*=CreatureID::NONE*/)
+void CCallback::giveObjectToModule(const CGObjectInstance *obj, shared_ptr<CAutomationModule> module)
 {
-	throw std::exception("The method or operation is not implemented.");
-}
-
-void CAutomationCallback::swapGarrisonHero(const CGTownInstance *town)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-void CAutomationCallback::trade(const CGObjectInstance *market, EMarketMode::EMarketMode mode, int id1, int id2, int val1, const CGHeroInstance *hero /*= NULL*/)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-int CAutomationCallback::selectionMade(int selection, QueryID queryID)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-int CAutomationCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
-
-int CAutomationCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
+	if(module->receivedObjects.hasObject(obj))
+		return;
 
-int CAutomationCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
-{
-	throw std::exception("The method or operation is not implemented.");
+	module->receivedObjects.add(obj);
+	module->newObjectReceived(obj);
 }
 
-int CAutomationCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)
+void CCallback::takeObjectFromModule(const CGObjectInstance *obj, shared_ptr<CAutomationModule> module)
 {
-	throw std::exception("The method or operation is not implemented.");
-}
+	if(!module->receivedObjects.hasObject(obj))
+		return;
 
-bool CAutomationCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)
-{
-	throw std::exception("The method or operation is not implemented.");
+	module->receivedObjects.remove(obj);
+	module->objectTakenAway(obj);
 }
 
-bool CAutomationCallback::assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
+void CCallback::sendMessageToModule(shared_ptr<CAutomationModule> module, const boost::any &message)
 {
-	throw std::exception("The method or operation is not implemented.");
+	module->receivedMessage(message);
 }
 
-bool CAutomationCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
+std::vector<shared_ptr<CAutomationModule>> CCallback::getAutomationInterfaces()
 {
-	throw std::exception("The method or operation is not implemented.");
-}
+	std::vector<shared_ptr<CAutomationModule>> ret;
+	BOOST_FOREACH(auto &interfacePtr, cl->additionalPlayerInts[*player])
+		if(auto automationModule = std::dynamic_pointer_cast<CAutomationModule>(interfacePtr))
+			ret.push_back(automationModule);
 
-void CAutomationCallback::endTurn()
-{
-	throw std::exception("The method or operation is not implemented.");
+	return ret;
 }
 
-void CAutomationCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
+void CCallback::executeAutomation()
 {
-	throw std::exception("The method or operation is not implemented.");
+	BOOST_FOREACH(auto module, getAutomationInterfaces())
+		module->execute();
 }
 
-void CAutomationCallback::setFormation(const CGHeroInstance * hero, bool tight)
+CBattleCallback::CBattleCallback(CGameState *GS, boost::optional<PlayerColor> Player, CClient *C )
 {
-	throw std::exception("The method or operation is not implemented.");
+	gs = GS;
+	player = Player;
+	cl = C;
 }
 
-void CAutomationCallback::setSelection(const CArmedInstance * obj)
+bool CBattleCallback::battleMakeTacticAction( BattleAction * action )
 {
-	throw std::exception("The method or operation is not implemented.");
+	assert(cl->gs->curB->tacticDistance);
+	MakeAction ma;
+	ma.ba = *action;
+	sendRequest(&ma);
+	return true;
 }
 
-void CAutomationCallback::buildBoat(const IShipyard *obj)
-{
-	throw std::exception("The method or operation is not implemented.");
-}
+// bool CAutomationCallback::moveHero(const CGHeroInstance *h, int3 dst)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// bool CAutomationCallback::dismissHero(const CGHeroInstance * hero)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::dig(const CGObjectInstance *hero)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos /*= int3(-1, -1, -1) */)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// bool CAutomationCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::recruitCreatures(const CGObjectInstance *obj, CreatureID ID, ui32 amount, si32 level/*=-1*/)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// bool CAutomationCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID/*=CreatureID::NONE*/)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::swapGarrisonHero(const CGTownInstance *town)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::trade(const CGObjectInstance *market, EMarketMode::EMarketMode mode, int id1, int id2, int val1, const CGHeroInstance *hero /*= NULL*/)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// int CAutomationCallback::selectionMade(int selection, QueryID queryID)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// int CAutomationCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// int CAutomationCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// int CAutomationCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// int CAutomationCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// bool CAutomationCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// bool CAutomationCallback::assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// bool CAutomationCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::endTurn()
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::setFormation(const CGHeroInstance * hero, bool tight)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::setSelection(const CArmedInstance * obj)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// void CAutomationCallback::buildBoat(const IShipyard *obj)
+// {
+// 	throw std::exception("The method or operation is not implemented.");
+// }
+// 
+// shared_ptr<CAutomationModule> CAutomationCallback::getModule()
+// {
+// 	return automationModule.lock();
+// }
 
-shared_ptr<CAutomationInterface> CAutomationCallback::getModule()
+CAutomationCallback::CAutomationCallback(CGameState * GS, boost::optional<PlayerColor> Player, CClient *C, shared_ptr<CAutomationModule> module)
+	: CCallback(GS, Player, C)
 {
-	return automationModule.lock();
-}
+	automationModule = module;
+}

+ 17 - 4
CCallback.h

@@ -29,7 +29,8 @@ struct CPathsInfo;
 struct CPack;
 class IBattleEventsReceiver;
 class IGameEventsReceiver;
-class CAutomationInterface;
+class CAutomationModule;
+struct ObjectsCeded;
 
 class IBattleCallback
 {
@@ -120,6 +121,15 @@ public:
 	void unregisterGameInterface(shared_ptr<IGameEventsReceiver> gameEvents);
 	void unregisterBattleInterface(shared_ptr<IBattleEventsReceiver> battleEvents);
 
+	virtual shared_ptr<CAutomationModule> createAutomationModule(const std::string &moduleName, const ObjectsCeded &objects, const boost::any &initialMessage = boost::none);
+	virtual void installAutomationModule(shared_ptr<CAutomationModule> module, const ObjectsCeded &objects);
+	virtual void removeAutomationModule(shared_ptr<CAutomationModule> module);
+	virtual void giveObjectToModule(const CGObjectInstance *obj, shared_ptr<CAutomationModule> module);
+	virtual void takeObjectFromModule(const CGObjectInstance *obj, shared_ptr<CAutomationModule> module);
+	virtual void sendMessageToModule(shared_ptr<CAutomationModule> module, const boost::any &message);
+	virtual std::vector<shared_ptr<CAutomationModule>> getAutomationInterfaces();
+	virtual void executeAutomation();
+
 	void unregisterMyInterface(); //stops delivering information about game events to that player's interface -> can be called ONLY after victory/loss
 
 //commands
@@ -159,15 +169,18 @@ public:
 
 class CAutomationCallback : public CCallback
 {
-	std::weak_ptr<CAutomationInterface> automationModule;
+	std::weak_ptr<CAutomationModule> automationModule;
 
-	shared_ptr<CAutomationInterface> getModule();
+	shared_ptr<CAutomationModule> getModule();
 
 	bool canAccessHero(const CGHeroInstance *h);
 	bool canRecruitAt(const CGDwelling *dwelling);
 
 public:
+	CAutomationCallback(CGameState * GS, boost::optional<PlayerColor> Player, CClient *C, shared_ptr<CAutomationModule> module);
+
 	//overloads
+	/*
 	virtual bool moveHero(const CGHeroInstance *h, int3 dst);
 	virtual bool dismissHero(const CGHeroInstance * hero);
 	virtual void dig(const CGObjectInstance *hero);
@@ -190,7 +203,7 @@ public:
 	virtual void buyArtifact(const CGHeroInstance *hero, ArtifactID aid);
 	virtual void setFormation(const CGHeroInstance * hero, bool tight);
 	virtual void setSelection(const CArmedInstance * obj);
-	virtual void buildBoat(const IShipyard *obj);
+	virtual void buildBoat(const IShipyard *obj);*/
 
 
 	//new, automation specific

+ 17 - 0
VCMI_VS11.sln

@@ -40,6 +40,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EmptyAI", "AI\EmptyAI\Empty
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VisitObjects", "AI\VisitObjects\VisitObjects.vcxproj", "{D3126B35-650E-46C8-A336-1D6640FBFFD0}"
+	ProjectSection(ProjectDependencies) = postProject
+		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -163,6 +168,18 @@ Global
 		{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
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Debug|Win32.ActiveCfg = Debug|Win32
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Debug|Win32.Build.0 = Debug|Win32
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Debug|x64.ActiveCfg = Debug|x64
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Debug|x64.Build.0 = Debug|x64
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.RD|Win32.ActiveCfg = RD|Win32
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.RD|Win32.Build.0 = RD|Win32
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.RD|x64.ActiveCfg = RD|x64
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.RD|x64.Build.0 = RD|x64
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Release|Win32.ActiveCfg = RD|Win32
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Release|Win32.Build.0 = RD|Win32
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Release|x64.ActiveCfg = RD|x64
+		{D3126B35-650E-46C8-A336-1D6640FBFFD0}.Release|x64.Build.0 = RD|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 1
client/CMT.cpp

@@ -720,7 +720,7 @@ void processCommand(const std::string &message)
 	else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
-		LOCPLINT->cb->sendMessage(message);
+		LOCPLINT->receivedConsoleMessage(message);
 	}
 }
 

+ 35 - 0
client/CPlayerInterface.cpp

@@ -2180,6 +2180,8 @@ void CPlayerInterface::acceptTurn()
 	if(howManyPeople > 1)
 		adventureInt->startTurn();
 
+	cb->executeAutomation();
+
 	//select first hero if available.
 	//TODO: check if hero is slept
 	adventureInt->heroList.update();
@@ -2480,6 +2482,39 @@ void CPlayerInterface::proposeLoadingGame()
 	showYesNoDialog(CGI->generaltexth->allTexts[68], [this] { sendCustomEvent(RETURN_TO_MENU_LOAD); }, 0, false);
 }
 
+void CPlayerInterface::receivedConsoleMessage(const std::string &msg)
+{
+	std::istringstream hlp(msg);
+	std::string s1, s2;
+	hlp >> s1 >> s2;
+
+	if(s1 == "create")
+	{
+		ObjectsCeded oc;
+		oc.add(adventureInt->selection);
+		try
+		{
+			cb->createAutomationModule(s2, oc);
+		}
+		catch(std::exception &e)
+		{
+			logGlobal->errorStream() << "Cannot create module " << s2 << ": " << e.what();
+		}
+	}
+	else if(s1 == "exec")
+	{
+		cb->executeAutomation();
+	}
+	else if(s1 == "give")
+	{
+		auto module = cb->getAutomationInterfaces().front();
+		cb->giveObjectToModule(adventureInt->selection, module);
+	}
+
+	BOOST_FOREACH(auto module, cb->getAutomationInterfaces())
+		cb->sendMessageToModule(module, msg);
+}
+
 CPlayerInterface::SpellbookLastSetting::SpellbookLastSetting()
 {
 	spellbookLastPageBattle = spellbokLastPageAdvmap = 0;

+ 2 - 0
client/CPlayerInterface.h

@@ -132,6 +132,8 @@ public:
 	void initializeHeroTownList();
 	int getLastIndex(std::string namePrefix);
 
+	void receivedConsoleMessage(const std::string &msg);
+
 	//overridden funcs from CGameInterface
 	void buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) OVERRIDE; //what: 1 - built, 2 - demolished
 	void stackChagedCount(const StackLocation &location, const TQuantity &change, bool isAbsolute) OVERRIDE; //if absolute, change is the new count; otherwise count was modified by adding change

+ 41 - 0
lib/CGameInterface.cpp

@@ -106,6 +106,11 @@ shared_ptr<CScriptingModule> CDynLibHandler::getNewScriptingModule(std::string d
 	return createAny<CScriptingModule>(dllname, "GetNewModule");
 }
 
+shared_ptr<CAutomationModule> CDynLibHandler::getNewAutomationModule(std::string dllname)
+{
+	return createAny<CAutomationModule>(VCMIDirs::get().libraryPath() + "/AI/" + VCMIDirs::get().libraryName(dllname), "GetNewAutomationModule");
+}
+
 BattleAction CGlobalAI::activeStack( const CStack * stack )
 {
 	BattleAction ba; ba.actionType = Battle::DEFEND;
@@ -255,3 +260,39 @@ void CAutomationModule::receivedMessage(const boost::any &msg)
 {
 
 }
+
+void CAutomationModule::execute()
+{
+	executeInternal();
+}
+
+void CAutomationModule::automationInit(shared_ptr<CAutomationCallback> cb, const ObjectsCeded &objs)
+{
+	this->cb = cb;
+	receivedObjects = objs;
+}
+
+void CAutomationModule::newObjectReceived(const CGObjectInstance *obj)
+{
+
+}
+
+void CAutomationModule::objectTakenAway(const CGObjectInstance *obj)
+{
+
+}
+
+const std::vector<const CGObjectInstance*> & CAutomationModule::getMyObjects() const
+{
+	return receivedObjects.objects;
+}
+
+std::vector<const CGHeroInstance*> CAutomationModule::getMyHeroes() const
+{
+	std::vector<const CGHeroInstance*> ret;
+	BOOST_FOREACH(auto obj, getMyObjects())
+		if(auto h = dynamic_cast<const CGHeroInstance*>(obj))
+			ret.push_back(h);
+
+	return ret;
+}

+ 44 - 22
lib/CGameInterface.h

@@ -53,6 +53,28 @@ template <typename Serializer> class COSer;
 struct ArtifactLocation;
 class CScriptingModule;
 class CAutomationModule;
+class CClient;
+class CAutomationCallback;
+
+struct ObjectsCeded
+{
+	std::vector<const CGObjectInstance *> objects;
+
+	bool hasObject(const CGObjectInstance *obj) const
+	{
+		return vstd::contains(objects, obj);
+	}
+	void remove(const CGObjectInstance *obj)
+	{
+		auto it = range::find(objects, obj);
+		if(it != objects.end())
+			objects.erase(it);
+	}
+	void add(const CGObjectInstance *obj)
+	{
+		objects.push_back(obj);
+	}
+};
 
 
 class DLL_LINKAGE CBattleGameInterface : public IBattleEventsReceiver
@@ -101,6 +123,7 @@ public:
 	static shared_ptr<CGlobalAI> getNewAI(std::string dllname);
 	static shared_ptr<CBattleGameInterface> getNewBattleAI(std::string dllname);
 	static shared_ptr<CScriptingModule> getNewScriptingModule(std::string dllname);
+	static shared_ptr<CAutomationModule> getNewAutomationModule(std::string dllname);
 };
 
 class DLL_LINKAGE CGlobalAI : public CGameInterface // AI class (to derivate)
@@ -146,33 +169,32 @@ public:
 	virtual void loadGame(CISer<CLoadFile> &h, const int version); //loading
 };
 
-struct ReceivedObjects
+class DLL_LINKAGE CAutomationModule : public IGameEventsReceiver
 {
-	std::vector<const CGObjectInstance *> objects;
+	ObjectsCeded receivedObjects;
 
-	bool hasObject(const CGObjectInstance *obj) const
-	{
-		return vstd::contains(objects, obj);
-	}
-	bool remove(const CGObjectInstance *obj)
-	{
-		auto it = range::find(objects, obj);
-		if(it != objects.end())
-			objects.erase(it);
-	}
-	bool add(const CGObjectInstance *obj)
-	{
-		objects.push_back(obj);
-	}
-};
-
-class DLL_LINKAGE CAutomationModule : public CGameInterface
-{
-	ReceivedObjects receivedObjects;
+protected:
+	const std::vector<const CGObjectInstance*> &getMyObjects() const;
+	std::vector<const CGHeroInstance*> getMyHeroes() const;
+	shared_ptr<CAutomationCallback> cb;
 
 public:
+	void automationInit(shared_ptr<CAutomationCallback> cb, const ObjectsCeded &objs);
 
+
+	virtual bool needsWaitingForRealize() const {return true;}
+	virtual bool needsUnlockingGs() const {return false;}
+
+	virtual void newObjectReceived(const CGObjectInstance *obj);
+	virtual void objectTakenAway(const CGObjectInstance *obj);
+
+	virtual void modulePrepared(){};
 	virtual void receivedMessage(const boost::any &msg);
+	virtual void executeInternal() = 0;
+
+	void execute();
 
-	friend class CAutomationModule;
+	//friend class CAutomationCallback;
+	friend class CCallback;
+	//friend class CClient;
 };

+ 3 - 0
lib/VCMI_lib.vcxproj

@@ -291,6 +291,9 @@
     <ClInclude Include="VCMI_Lib.h" />
     <ClInclude Include="VCMIDirs.h" />
   </ItemGroup>
+  <ItemGroup>
+    <None Include="ClassDiagram1.cd" />
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>