Kaynağa Gözat

ERM is now a separate project, linking to ./Scripting/ERM.dll and consisting all ERM* files from ./lib.
Little changes around handling ERM. New class in lib: CFileUtility, so the code for scanning files with given extension is not duplicated.

Michał W. Urbańczyk 14 yıl önce
ebeveyn
işleme
580c4e4c9b

+ 24 - 10
CGameInterface.cpp

@@ -20,7 +20,7 @@
  */
 
 template<typename rett>
-rett * createAnyAI(std::string dllname, std::string methodName)
+rett * createAny(std::string dllname, std::string methodName)
 {
 	char temp[50];
 	rett * ret=NULL;
@@ -29,24 +29,25 @@ rett * createAnyAI(std::string dllname, std::string methodName)
 
 	std::string dllPath;
 
+	//TODO unify at least partially (code duplication)
 #ifdef _WIN32
-	dllPath = LIB_DIR "/" +dllname+".dll";
+	dllPath = dllname;
 	HINSTANCE dll = LoadLibraryA(dllPath.c_str());
 	if (!dll)
 	{
-		tlog1 << "Cannot open AI library ("<<dllPath<<"). Throwing..."<<std::endl;
-		throw new std::string("Cannot open AI library");
+		tlog1 << "Cannot open dynamic library ("<<dllPath<<"). Throwing..."<<std::endl;
+		throw new std::string("Cannot open dynamic library");
 	}
 	//int len = dllname.size()+1;
 	getName = (void(*)(char*))GetProcAddress(dll,"GetAiName");
 	getAI = (rett*(*)())GetProcAddress(dll,methodName.c_str());
 #else
-	dllPath = LIB_DIR "/" + dllname + ".so";
+	dllPath = dllname;
 	void *dll = dlopen(dllPath.c_str(), RTLD_LOCAL | RTLD_LAZY);
 	if (!dll)
 	{
-		tlog1 << "Cannot open AI library ("<<dllPath<<"). Throwing..."<<std::endl;
-		throw new std::string("Cannot open AI library");
+		tlog1 << "Cannot open dynamic library ("<<dllPath<<"). Throwing..."<<std::endl;
+		throw new std::string("Cannot open dynamic library");
 	}
 	getName = (void(*)(char*))dlsym(dll,"GetAiName");
 	getAI = (rett*(*)())dlsym(dll,methodName.c_str());
@@ -58,20 +59,33 @@ rett * createAnyAI(std::string dllname, std::string methodName)
 	if(!ret)
 		tlog1 << "Cannot get AI!\n";
 
-	ret->dllName = dllname;	 
 	return ret;
 }
 
-CGlobalAI * CAIHandler::getNewAI(std::string dllname)
+
+template<typename rett>
+rett * createAnyAI(std::string dllname, std::string methodName)
+{
+	rett* ret = createAny<rett>(LIB_DIR "/" + dllname + '.' + LIB_EXT, methodName);
+	ret->dllName = dllname;	
+	return ret;
+}
+
+CGlobalAI * CDynLibHandler::getNewAI(std::string dllname)
 {
 	return createAnyAI<CGlobalAI>(dllname, "GetNewAI");
 }
 
-CBattleGameInterface * CAIHandler::getNewBattleAI(std::string dllname )
+CBattleGameInterface * CDynLibHandler::getNewBattleAI(std::string dllname )
 {
 	return createAnyAI<CBattleGameInterface>(dllname, "GetNewBattleAI");
 }
 
+CScriptingModule * CDynLibHandler::getNewScriptingModule(std::string dllname)
+{
+	return createAny<CScriptingModule>(dllname, "GetNewModule");
+}
+
 BattleAction CGlobalAI::activeStack( const CStack * stack )
 {
 	BattleAction ba; ba.actionType = BattleAction::DEFEND;

+ 4 - 1
CGameInterface.h

@@ -54,6 +54,7 @@ typedef si32 TQuantity;
 template <typename Serializer> class CISer;
 template <typename Serializer> class COSer;
 struct ArtifactLocation;
+class CScriptingModule;
 
 class CBattleGameInterface : public IBattleEventsReceiver
 {
@@ -82,12 +83,14 @@ public:
 	virtual void serialize(CISer<CLoadFile> &h, const int version){}; //loading
 };
 
-class CAIHandler
+class CDynLibHandler
 {
 public:
 	static CGlobalAI * getNewAI(std::string dllname);
 	static CBattleGameInterface * getNewBattleAI(std::string dllname);
+	static CScriptingModule * getNewScriptingModule(std::string dllname);
 };
+
 class CGlobalAI : public CGameInterface // AI class (to derivate)
 {
 public:

+ 115 - 0
Scripting/ERM/ERM.vcxproj

@@ -0,0 +1,115 @@
+<?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="RD|Win32">
+      <Configuration>RD</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{8F202F43-106D-4F63-AD9D-B1D43E803E8C}</ProjectGuid>
+    <RootNamespace>ERM</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>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </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" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </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" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <OutDir>$(SolutionDir)$(Configuration)\bin\Scripting\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(SolutionDir)\Scripting\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>../../../include;</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>../../../libs; ../../;</AdditionalLibraryDirectories>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>../../../include;</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>../../../libs; ../../;</AdditionalLibraryDirectories>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>../../../include;</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>../../../libs; ../../;</AdditionalLibraryDirectories>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="ERMInterpreter.h" />
+    <ClInclude Include="ERMParser.h" />
+    <ClInclude Include="ERMScriptModule.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="ERMInterpreter.cpp" />
+    <ClCompile Include="ERMParser.cpp" />
+    <ClCompile Include="ERMScriptModule.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 102 - 99
lib/ERMInterpreter.cpp → Scripting/ERM/ERMInterpreter.cpp

@@ -1,6 +1,5 @@
-#define VCMI_DLL
 #include "ERMInterpreter.h"
-
+#include <cctype>
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
@@ -9,9 +8,9 @@
 #include <boost/assign/std/vector.hpp> 
 #include <boost/assign/list_of.hpp>
 
-#include "CObjectHandler.h"
-#include "CHeroHandler.h"
-#include "CCreatureHandler.h"
+#include "../../lib/CObjectHandler.h"
+#include "../../lib/CHeroHandler.h"
+#include "../../lib/CCreatureHandler.h"
 
 /*
  * ERMInterpreter.cpp, part of VCMI engine
@@ -745,9 +744,14 @@ struct HEPerformer : StandardReceiverVisitor<const CGHeroInstance *>
 					if(erm->getIexp(params[0]).getInt() == 0)
 					{
 						int slot = erm->getIexp(params[1]).getInt();
+						const CStackInstance *stack = identifier->getStackPtr(slot);
 						if(params[2].which() == 6) //varp
 						{
-							erm->getIexp(boost::get<ERM::TVarpExp>(params[2])).setTo(identifier->getCreature(slot)->idNumber);
+							IexpValStr lhs = erm->getIexp(boost::get<ERM::TVarpExp>(params[2]));
+							if(stack)
+								lhs.setTo(stack->getCreatureID());
+							else
+								lhs.setTo(-1);
 						}
 						else
 							throw EScriptExecError("Setting stack creature type is not implemented!");
@@ -787,89 +791,126 @@ struct IF_MPerformer : StandardBodyOptionItemVisitor<IFPerformer>
 	void operator()(TStringConstant const& cmp) const OVERRIDE;
 };
 
-struct IFPerformer : StandardReceiverVisitor<TUnusedType>
+
+//according to the ERM help:
+//"%%" -> "%"
+//"%V#" -> current value of # flag.
+//"%Vf"..."%Vt" -> current value of corresponding variable.
+//"%W1"..."%W100" -> current value of corresponding hero variable.
+//"%X1"..."%X16" -> current value of corresponding function parameter.
+//"%Y1"..."%Y100" -> current value of corresponding local variable.
+//"%Z1"..."%Z500" -> current value of corresponding string variable.
+//"%$macro$" -> macro name of corresponding variable
+//"%Dd" -> current day of week
+//"%Dw" -> current week
+//"%Dm" -> current month
+//"%Da" -> current day from beginning of the game
+//"%Gc" -> the color of current gamer in text
+struct StringFormatter
 {
-	IFPerformer(ERMInterpreter * _interpr) : StandardReceiverVisitor<TUnusedType>(_interpr, 0)
-	{}
-	using StandardReceiverVisitor<TUnusedType>::operator();
+	int pos;
+	int tokenLength;
+	int percentPos;
+	int charsToReplace;
+	std::string &msg;
 
+	StringFormatter(std::string &MSG) : msg(MSG), pos(0) {}
 
-	void operator()(TNormalBodyOption const& trig) const OVERRIDE
+	static void format(std::string &msg)
 	{
-		std::string message; //to be shown
-		switch(trig.optionCode)
-		{
-		case 'M': //Show the message (Text) or contents of z$ variable on the screen immediately.
-			performOptionTakingOneParamter<IF_MPerformer>(trig.params);
-			break;
-		default:
-			break;
-		}
+		StringFormatter sf(msg);
+		sf.format();
 	}
 
 	// startpos is the first digit
 	// digits will be converted to number and returned
-	static int getNum(std::string &msg, int numStart, int &digitsUsed) 
+	// ADDITIVE on digitsUsed
+	int getNum() 
 	{
+		int toAdd = 0;
+		int numStart = percentPos + 2;
 		int numEnd = msg.find_first_not_of("1234567890", numStart);
 
 		if(numEnd == std::string::npos)
-			digitsUsed = msg.size() - numStart;
+			toAdd = msg.size() - numStart;
 		else
-			digitsUsed = numEnd - numStart;
+			toAdd = numEnd - numStart;
 
-		return boost::lexical_cast<int>(msg.substr(numStart, digitsUsed));
+		charsToReplace += toAdd;
+		return boost::lexical_cast<int>(msg.substr(numStart, toAdd));
 	}
 
-	static void formatMessage(std::string &msg)
+	void format()
 	{
-		int pos = 0; //index of the first not yet processed character in string
-
-		//according to the ERM help:
-		//"%%" -> "%"
-		//"%V#" -> current value of # flag.
-		//"%Vf"..."%Vt" -> current value of corresponding variable.
-		//"%W1"..."%W100" -> current value of corresponding hero variable.
-		//"%X1"..."%X16" -> current value of corresponding function parameter.
-		//"%Y1"..."%Y100" -> current value of corresponding local variable.
-		//"%Z1"..."%Z500" -> current value of corresponding string variable.
-		//"%$macro$" -> macro name of corresponding variable
-		//"%Dd" -> current day of week
-		//"%Dw" -> current week
-		//"%Dm" -> current month
-		//"%Da" -> current day from beginning of the game
-		//"%Gc" -> the color of current gamer in text
-		
 		while(pos < msg.size())
 		{
-			int percentPos = msg.find_first_of('%', pos);
+			percentPos = msg.find_first_of('%', pos);
+			charsToReplace = 1; //at least the same '%' needs to be replaced
+
+			std::ostringstream replaceWithWhat;
+
 			if(percentPos == std::string::npos) //processing done?
 				break;
 
 			if(percentPos + 1 >= msg.size()) //at least one character after % is required
 				throw EScriptExecError("Formatting error: % at the end of string!"); 
-;
+
+			charsToReplace++; //the sign after % is consumed
 			switch(msg[percentPos+1])
 			{
 			case '%':
-				msg.erase(percentPos+1, 1); //just delete superfluous %
+				replaceWithWhat << '%';
 				break;
-			case 'X':
+			case 'F':
+				replaceWithWhat << erm->ermGlobalEnv->getFlag(getNum());
+				break;
+			case 'V':
+				if(std::isdigit(msg[percentPos + 2]))
+					replaceWithWhat << erm->ermGlobalEnv->getStandardVar(getNum());
+				else
 				{
-					int digits;
-					int varNum = getNum(msg, percentPos+2, digits);
-					msg.replace(percentPos, digits+2, boost::lexical_cast<std::string>(erm->getVar("x", varNum).getInt()));
+					charsToReplace++;
+					replaceWithWhat << erm->ermGlobalEnv->getQuickVar(msg[percentPos+2]);
 				}
+			case 'X':
+				replaceWithWhat << erm->getVar("x", getNum()).getInt();
 				break;
+			case 'Z':
+				replaceWithWhat << erm->ermGlobalEnv->getZVar(getNum());
+				break;
+			default:
+				throw EScriptExecError("Formatting error: unrecognized token indicator after %!");
 			}
-			pos++;
+			msg.replace(percentPos, charsToReplace, replaceWithWhat.str());
+			pos = percentPos + 1;
+		}
+	}
+};
+
+struct IFPerformer : StandardReceiverVisitor<TUnusedType>
+{
+	IFPerformer(ERMInterpreter * _interpr) : StandardReceiverVisitor<TUnusedType>(_interpr, 0)
+	{}
+	using StandardReceiverVisitor<TUnusedType>::operator();
+
+
+	void operator()(TNormalBodyOption const& trig) const OVERRIDE
+	{
+		std::string message; //to be shown
+		switch(trig.optionCode)
+		{
+		case 'M': //Show the message (Text) or contents of z$ variable on the screen immediately.
+			performOptionTakingOneParamter<IF_MPerformer>(trig.params);
+			break;
+		default:
+			break;
 		}
 	}
 
 	void showMessage(const std::string &msg)
 	{
 		std::string msgToFormat = msg;
-		IFPerformer::formatMessage(msgToFormat);
+		StringFormatter::format(msgToFormat);
 		acb->showInfoDialog(msgToFormat, icb->getLocalPlayer());
 	}
 };
@@ -1937,54 +1978,6 @@ void ERMInterpreter::setCurrentlyVisitedObj( int3 pos )
 	ermGlobalEnv->getStandardVar(1000) = pos.z;
 }
 
-struct StringProcessHLP
-{
-	int extractNumber(const std::string & token)
-	{
-		return atoi(token.substr(1).c_str());
-	}
-	template <typename T>
-	void replaceToken(std::string ret, int tokenBegin, int tokenEnd, T val)
-	{
-		ret.replace(tokenBegin, tokenEnd, boost::lexical_cast<std::string>(val));
-	}
-};
-
-std::string ERMInterpreter::processERMString( std::string ermstring )
-{
-	StringProcessHLP hlp;
-	std::string ret = ermstring;
-	int curPos = 0;
-	while((curPos = ret.find('%', curPos)) != std::string::npos)
-	{
-		curPos++;
-		int tokenEnd = ret.find(' ', curPos);
-		std::string token = ret.substr(curPos, tokenEnd - curPos);
-		if(token.size() == 0)
-		{
-			throw EScriptExecError("Empty token not allowed!");
-		}
-
-		switch(token[0])
-		{
-		case '%':
-			ret.erase(curPos);
-			break;
-		case 'F':
-			hlp.replaceToken(ret, curPos, tokenEnd, ermGlobalEnv->getFlag(hlp.extractNumber(token)));
-			break;
-		case 'V':
-			hlp.replaceToken(ret, curPos, tokenEnd, ermGlobalEnv->getStandardVar(hlp.extractNumber(token)));
-			break;
-		default:
-			throw EScriptExecError("Unrecognized token in string");
-			break;
-		}
-
-	}
-	return ret;
-}
-
 const std::string ERMInterpreter::triggerSymbol = "trigger";
 const std::string ERMInterpreter::postTriggerSymbol = "postTrigger";
 const std::string ERMInterpreter::defunSymbol = "defun";
@@ -2747,6 +2740,16 @@ void ERMInterpreter::executeUserCommand(const std::string &cmd)
 	tlog0 << "ERM here: received command: " << cmd << std::endl;
 }
 
+void ERMInterpreter::giveInfoCB(CPrivilagedInfoCallback *cb)
+{
+	icb = cb;
+}
+
+void ERMInterpreter::giveActionCB(IGameEventRealizer *cb)
+{
+	acb = cb;
+}
+
 namespace VERMInterpreter
 {
 	VOption convertToVOption(const ERM::TVOption & tvo)

+ 4 - 2
lib/ERMInterpreter.h → Scripting/ERM/ERMInterpreter.h

@@ -1,7 +1,6 @@
 #pragma once
-#include "../global.h"
+#include "../../global.h"
 #include "ERMParser.h"
-#include "IGameEventsReceiver.h"
 #include "ERMScriptModule.h"
 
 /*
@@ -841,6 +840,8 @@ public:
 	virtual void heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visitedObj, bool start) OVERRIDE;
 	virtual void init() OVERRIDE;//sets up environment etc.
 	virtual void executeUserCommand(const std::string &cmd) OVERRIDE;
+	virtual void giveInfoCB(CPrivilagedInfoCallback *cb) OVERRIDE;
+	virtual void giveActionCB(IGameEventRealizer *cb) OVERRIDE;
 
 	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) OVERRIDE;
 
@@ -854,4 +855,5 @@ public:
 		else
 			throw VERMInterpreter::EScriptExecError("Wrong cast attempted, object is not of a desired type!");
 	}
+
 };

+ 0 - 1
lib/ERMParser.cpp → Scripting/ERM/ERMParser.cpp

@@ -1,4 +1,3 @@
-#define VCMI_DLL
 #include "ERMParser.h"
 #include <boost/version.hpp>
 //To make compilation with older boost versions possible

+ 1 - 1
lib/ERMParser.h → Scripting/ERM/ERMParser.h

@@ -1,5 +1,5 @@
 #pragma once
-#include "../global.h"
+#include "../../global.h"
 #include <fstream>
 #include <boost/variant.hpp>
 #include <boost/optional.hpp>

+ 22 - 0
Scripting/ERM/ERMScriptModule.cpp

@@ -0,0 +1,22 @@
+#include "ERMScriptModule.h"
+#include "ERMInterpreter.h"
+
+IGameEventRealizer *acb;
+CPrivilagedInfoCallback *icb;
+
+
+#ifdef __GNUC__
+#define strcpy_s(a, b, c) strncpy(a, c, b)
+#endif
+
+const char *g_cszAiName = "(V)ERM interpreter";
+
+extern "C" DLL_F_EXPORT void GetAiName(char* name)
+{
+	strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
+}
+
+extern "C" DLL_F_EXPORT CScriptingModule* GetNewModule()
+{
+	return new ERMInterpreter();
+}

+ 5 - 0
Scripting/ERM/ERMScriptModule.h

@@ -0,0 +1,5 @@
+#pragma once
+
+#include "../../lib/CScriptingModule.h"
+extern IGameEventRealizer *acb;
+extern CPrivilagedInfoCallback *icb;

+ 8 - 0
VCMI_VS10.sln

@@ -14,6 +14,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StupidAI", "AI\StupidAI\Stu
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ERM", "Scripting\ERM\ERM.vcxproj", "{8F202F43-106D-4F63-AD9D-B1D43E803E8C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -51,6 +53,12 @@ Global
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.RD|Win32.Build.0 = RD|Win32
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Release|Win32.ActiveCfg = Release|Win32
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Release|Win32.Build.0 = Release|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|Win32.Build.0 = Debug|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|Win32.ActiveCfg = RD|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|Win32.Build.0 = RD|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Release|Win32.ActiveCfg = Release|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 1
client/CMT.cpp

@@ -45,7 +45,7 @@
 #include "../lib/CObjectHandler.h"
 #include <boost/program_options.hpp>
 #include "../lib/CArtHandler.h"
-#include "../lib/ERMScriptModule.h"
+#include "../lib/CScriptingModule.h"
 
 #ifdef _WIN32
 #include "SDL_syswm.h"

+ 2 - 28
client/CPreGame.cpp

@@ -48,6 +48,7 @@
 #include <boost/thread/recursive_mutex.hpp>
 #include "../CThreadHelper.h"
 #include "CConfigHandler.h"
+#include "../lib/CFileUtility.h"
 
 /*
  * CPreGame.cpp, part of VCMI engine
@@ -846,34 +847,7 @@ void SelectionTab::filter( int size, bool selectFirst )
 
 void SelectionTab::getFiles(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext)
 {
-	if(!boost::filesystem::exists(dirname))
-	{
-		tlog1 << "Cannot find " << dirname << " directory!\n";
-	}
-	fs::path tie(dirname);
-	fs::directory_iterator end_iter;
-	for ( fs::directory_iterator file (tie); file!=end_iter; ++file )
-	{
-		if(fs::is_regular_file(file->status())
-			&& boost::ends_with(file->path().filename(), ext))
-		{
-			std::time_t date = 0;
-			try
-			{
-				date = fs::last_write_time(file->path());
-
-				out.resize(out.size()+1);
-				out.back().date = date;
-				out.back().name = file->path().string();
-			}
-			catch(...)
-			{
-				tlog2 << "\t\tWarning: very corrupted map file: " << file->path().string() << std::endl; 
-			}
-
-		}
-	}
-
+	CFileUtility::getFilesWithExt(out, dirname, ext);
 	allItems.resize(out.size());
 }
 

+ 1 - 7
client/CPreGame.h

@@ -30,6 +30,7 @@ class CCampaignState;
 class CConnection;
 struct CPackForSelectionScreen;
 struct PlayerInfo;
+struct FileInfo;
 
 namespace boost{ class thread; class recursive_mutex;}
 
@@ -66,13 +67,6 @@ public:
 	void moveTo(CMenuScreen *next);
 };
 
-/// Struct which stores name, date and a value which says if the file is located in LOD
-struct FileInfo
-{
-	std::string name; // file name with full path and extension
-	std::time_t date;
-	bool inLod; //tells if this file is located in Lod
-};
 
 /// Implementation of the chat box
 class CChatBox : public CIntObject

+ 20 - 13
client/Client.cpp

@@ -33,7 +33,8 @@
 #include "CPreGame.h"
 #include "CBattleInterface.h"
 #include "../CThreadHelper.h"
-#include "../lib/ERMScriptModule.h"
+#include "../lib/CScriptingModule.h"
+#include "../lib/CFileUtility.h"
 
 #define NOT_LIB
 #include "../lib/RegisterTypes.cpp"
@@ -394,7 +395,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 			CCallback *cb = new CCallback(gs,color,this);
 			if(!it->second.human) 
 			{
-				playerint[color] = static_cast<CGameInterface*>(CAIHandler::getNewAI(conf.cc.defaultPlayerAI));
+				playerint[color] = static_cast<CGameInterface*>(CDynLibHandler::getNewAI(conf.cc.defaultPlayerAI));
 			}
 			else 
 			{
@@ -408,7 +409,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 		else
 		{
 			CBattleCallback * cbc = new CBattleCallback(gs, color, this);
-			battleints[color] = CAIHandler::getNewBattleAI("StupidAI");
+			battleints[color] = CDynLibHandler::getNewBattleAI("StupidAI");
 			battleints[color]->init(cbc);
 		}
 	}
@@ -430,13 +431,19 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	serv->addStdVecItems(const_cast<CGameInfo*>(CGI)->state);
 	hotSeat = (humanPlayers > 1);
 
-
-	erm = getERMModule();
- 	privilagedGameEventReceivers.push_back(erm);
- 	privilagedBattleEventReceivers.push_back(erm);
- 	icb = this;
- 	acb = this;
- 	erm->init();
+	std::vector<FileInfo> scriptModules;
+	CFileUtility::getFilesWithExt(scriptModules, "./Scripting", ".dll");
+	BOOST_FOREACH(FileInfo &m, scriptModules)
+	{
+		CScriptingModule * nm = CDynLibHandler::getNewScriptingModule(m.name);
+		privilagedGameEventReceivers.push_back(nm);
+		privilagedBattleEventReceivers.push_back(nm);
+		nm->giveActionCB(this);
+		nm->giveInfoCB(this);
+		nm->init();
+
+		erm = nm; //something tells me that there'll at most one module and it'll be ERM
+	}
 }
 
 template <typename Handler>
@@ -472,14 +479,14 @@ void CClient::serialize( Handler &h, const int version )
 				if(pid == 255)
 				{
 					CBattleCallback * cbc = new CBattleCallback(gs, pid, this);
-					CBattleGameInterface *cbgi = CAIHandler::getNewBattleAI(dllname);
+					CBattleGameInterface *cbgi = CDynLibHandler::getNewBattleAI(dllname);
 					battleints[pid] = cbgi;
 					cbgi->init(cb);
 					//TODO? consider serialization 
 					continue;
 				}
 				else
-					nInt = CAIHandler::getNewAI(dllname);
+					nInt = CDynLibHandler::getNewAI(dllname);
 			}
 			else
 				nInt = new CPlayerInterface(pid);
@@ -591,7 +598,7 @@ void CClient::battleStarted(const BattleInfo * info)
 
 void CClient::loadNeutralBattleAI()
 {
-	battleints[255] = CAIHandler::getNewBattleAI(conf.cc.defaultBattleAI);
+	battleints[255] = CDynLibHandler::getNewBattleAI(conf.cc.defaultBattleAI);
 	battleints[255]->init(new CBattleCallback(gs, 255, this));
 }
 

+ 17 - 15
global.h

@@ -51,22 +51,24 @@ extern std::string NAME_AFFIX; //client / server
  * LIB_DIR is where the AI libraries reside (linux only)
  */
 #ifdef _WIN32
-#define DATA_DIR "."
-#define USER_DIR  "."
-#define BIN_DIR  "."
-#define LIB_DIR "AI"
-#define SERVER_NAME "VCMI_server.exe"
+	#define DATA_DIR "."
+	#define USER_DIR  "."
+	#define BIN_DIR  "."
+	#define LIB_DIR "AI"
+	#define SERVER_NAME "VCMI_server.exe"
+	#define LIB_EXT "dll"
 #else
-#ifndef DATA_DIR
-#error DATA_DIR undefined.
-#endif
-#ifndef BIN_DIR
-#error BIN_DIR undefined.
-#endif
-#ifndef LIB_DIR
-#error LIB_DIR undefined.
-#endif
-#define SERVER_NAME "vcmiserver"
+	#ifndef DATA_DIR
+	#error DATA_DIR undefined.
+	#endif
+	#ifndef BIN_DIR
+	#error BIN_DIR undefined.
+	#endif
+	#ifndef LIB_DIR
+	#error LIB_DIR undefined.
+	#endif
+	#define SERVER_NAME "vcmiserver"
+	#define LIB_EXT "so"
 #endif
 
 #ifdef _WIN32

+ 16 - 1
lib/CCreatureSet.cpp

@@ -268,7 +268,14 @@ void CCreatureSet::clear()
 const CStackInstance& CCreatureSet::getStack(TSlot slot) const
 {
 	assert(hasStackAtSlot(slot));
-	return *stacks.find(slot)->second;
+	return *getStackPtr(slot);
+}
+
+const CStackInstance* CCreatureSet::getStackPtr(TSlot slot) const
+{
+	if(hasStackAtSlot(slot))
+		return stacks.find(slot)->second;
+	else return NULL;
 }
 
 void CCreatureSet::eraseStack(TSlot slot)
@@ -914,6 +921,14 @@ void CStackInstance::deserializationFix()
 	setArmyObj(armyBackup);
 }
 
+int CStackInstance::getCreatureID() const
+{
+	if(type)
+		return type->idNumber;
+	else 
+		return -1;
+}
+
 CStackBasicDescriptor::CStackBasicDescriptor()
 {
 	type = NULL;

+ 3 - 1
lib/CCreatureSet.h

@@ -53,6 +53,7 @@ public:
 	std::string getQuantityTXT(bool capitalized = true) const;
 	int getExpRank() const;
 	si32 magicResistance() const;
+	int getCreatureID() const; //-1 if not available
 	void init();
 	CStackInstance();
 	CStackInstance(TCreature id, TQuantity count);
@@ -132,7 +133,8 @@ public:
 	bool setCreature (TSlot slot, TCreature type, TQuantity quantity) OVERRIDE; //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack
 	void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all.
 	
-	const CStackInstance& getStack(TSlot slot) const; 
+	const CStackInstance& getStack(TSlot slot) const; //stack must exist
+	const CStackInstance* getStackPtr(TSlot slot) const; //if stack doesn't exist, returns NULL
 	const CCreature* getCreature(TSlot slot) const; //workaround of map issue;
 	int getStackCount (TSlot slot) const;
 	expType getStackExperience(TSlot slot) const;

+ 46 - 0
lib/CFileUtility.cpp

@@ -0,0 +1,46 @@
+#define VCMI_DLL
+#include "CFileUtility.h"
+#include <boost/filesystem.hpp>   // includes all needed Boost.Filesystem declarations
+#include <boost/algorithm/string/predicate.hpp>
+
+namespace fs = boost::filesystem;
+
+CFileUtility::CFileUtility(void)
+{
+}
+
+
+CFileUtility::~CFileUtility(void)
+{
+}
+
+void CFileUtility::getFilesWithExt(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext)
+{
+	if(!fs::exists(dirname))
+	{
+		tlog1 << "Cannot find " << dirname << " directory!\n";
+	}
+	fs::path tie(dirname);
+	fs::directory_iterator end_iter;
+	for ( fs::directory_iterator file (tie); file!=end_iter; ++file )
+	{
+		if(fs::is_regular_file(file->status())
+			&& boost::ends_with(file->path().filename(), ext))
+		{
+			std::time_t date = 0;
+			try
+			{
+				date = fs::last_write_time(file->path());
+
+				out.resize(out.size()+1);
+				out.back().date = date;
+				out.back().name = file->path().string();
+			}
+			catch(...)
+			{
+				tlog2 << "\t\tWarning: very corrupted file: " << file->path().string() << std::endl; 
+			}
+
+		}
+	}
+}

+ 20 - 0
lib/CFileUtility.h

@@ -0,0 +1,20 @@
+#pragma once
+#include "../global.h"
+
+/// Struct which stores name, date and a value which says if the file is located in LOD
+struct FileInfo
+{
+	std::string name; // file name with full path and extension
+	std::time_t date;
+	bool inLod; //tells if this file is located in Lod
+};
+
+class DLL_EXPORT CFileUtility
+{
+public:
+	CFileUtility(void);
+	~CFileUtility(void);
+
+	static void getFilesWithExt(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext);
+};
+

+ 16 - 0
lib/CScriptingModule.h

@@ -0,0 +1,16 @@
+#pragma once
+#include "../global.h"
+#include "IGameEventsReceiver.h"
+#include "IGameCallback.h"
+
+class CScriptingModule : public IGameEventsReceiver, public IBattleEventsReceiver
+{
+public:
+	virtual void executeUserCommand(const std::string &cmd){};
+	virtual void init(){}; //called upon the start of game (after map randomization, before first turn)
+	virtual void giveActionCB(IGameEventRealizer *cb){}; 
+	virtual void giveInfoCB(CPrivilagedInfoCallback *cb){}; 
+
+	CScriptingModule(){}
+	virtual ~CScriptingModule(){}
+};

+ 0 - 20
lib/ERMScriptModule.cpp

@@ -1,20 +0,0 @@
-#define VCMI_DLL
-
-#include "ERMScriptModule.h"
-#include "ERMInterpreter.h"
-#include "CObjectHandler.h"
-
-IGameEventRealizer *acb;
-CPrivilagedInfoCallback *icb;
-
-
-CScriptingModule * getERMModule()
-{
-	CScriptingModule *ret = new ERMInterpreter();
-	return ret;
-}
-
-CScriptingModule::~CScriptingModule()
-{
-
-}

+ 0 - 23
lib/ERMScriptModule.h

@@ -1,23 +0,0 @@
-#pragma once
-
-#include "../global.h"
-#include "IGameEventsReceiver.h"
-#include "IGameCallback.h"
-
-class IGameEventRealizer;
-
-class ERMInterpreter;
-
-class DLL_EXPORT CScriptingModule : public IGameEventsReceiver, public IBattleEventsReceiver
-{
-public:
-	virtual void executeUserCommand(const std::string &cmd){};
-	virtual void init(){}; //called upon the start of game (after map randomization, before first turn)
-	virtual ~CScriptingModule();
-};
-
-extern DLL_EXPORT IGameEventRealizer *acb;
-extern DLL_EXPORT CPrivilagedInfoCallback *icb;
-
-
-DLL_EXPORT CScriptingModule *getERMModule();

+ 1 - 1
lib/VCMI_Lib.cpp

@@ -11,7 +11,7 @@
 #include "CBuildingHandler.h"
 #include "CSpellHandler.h"
 #include "CGeneralTextHandler.h"
-#include "ERMInterpreter.h"
+#include "IGameEventsReceiver.h"
 
 /*
  * VCMI_Lib.cpp, part of VCMI engine

+ 3 - 6
lib/VCMI_lib.vcxproj

@@ -172,6 +172,7 @@
     <ClCompile Include="CCreatureHandler.cpp" />
     <ClCompile Include="CCreatureSet.cpp" />
     <ClCompile Include="CDefObjInfoHandler.cpp" />
+    <ClCompile Include="CFileUtility.cpp" />
     <ClCompile Include="CGameState.cpp" />
     <ClCompile Include="CGeneralTextHandler.cpp" />
     <ClCompile Include="CHeroHandler.cpp" />
@@ -181,9 +182,6 @@
     <ClCompile Include="Connection.cpp" />
     <ClCompile Include="CSpellHandler.cpp" />
     <ClCompile Include="CTownHandler.cpp" />
-    <ClCompile Include="ERMInterpreter.cpp" />
-    <ClCompile Include="ERMParser.cpp" />
-    <ClCompile Include="ERMScriptModule.cpp" />
     <ClCompile Include="HeroBonus.cpp" />
     <ClCompile Include="IGameCallback.cpp" />
     <ClCompile Include="map.cpp" />
@@ -204,6 +202,7 @@
     <ClInclude Include="CCreatureHandler.h" />
     <ClInclude Include="CCreatureSet.h" />
     <ClInclude Include="CDefObjInfoHandler.h" />
+    <ClInclude Include="CFileUtility.h" />
     <ClInclude Include="CGameState.h" />
     <ClInclude Include="CGeneralTextHandler.h" />
     <ClInclude Include="CHeroHandler.h" />
@@ -214,11 +213,9 @@
     <ClInclude Include="CondSh.h" />
     <ClInclude Include="Connection.h" />
     <ClInclude Include="ConstTransitivePtr.h" />
+    <ClInclude Include="CScriptingModule.h" />
     <ClInclude Include="CSpellHandler.h" />
     <ClInclude Include="CTownHandler.h" />
-    <ClInclude Include="ERMInterpreter.h" />
-    <ClInclude Include="ERMParser.h" />
-    <ClInclude Include="ERMScriptModule.h" />
     <ClInclude Include="HeroBonus.h" />
     <ClInclude Include="IGameCallback.h" />
     <ClInclude Include="IGameEventsReceiver.h" />