Browse Source

Added VCAI and EmptyAI to the VS solution. Removed from it GeniusAI.
Fixed #329, #884, #885.

Michał W. Urbańczyk 13 years ago
parent
commit
f2642cb62c
9 changed files with 262 additions and 68 deletions
  1. 177 0
      AI/EmptyAI/EmptyAI.vcxproj
  2. 9 7
      AI/VCAI/VCAI.cpp
  3. 8 10
      VCMI_VS10.sln
  4. 1 1
      client/CMT.cpp
  5. 4 8
      client/CPreGame.cpp
  6. 1 1
      client/Client.cpp
  7. 50 31
      lib/BattleState.cpp
  8. 5 2
      lib/BattleState.h
  9. 7 8
      lib/NetPacksLib.cpp

+ 177 - 0
AI/EmptyAI/EmptyAI.vcxproj

@@ -0,0 +1,177 @@
+<?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>
+  <ItemGroup>
+    <ClCompile Include="CEmptyAI.cpp" />
+    <ClCompile Include="exp_funcs.cpp" />
+    <ClCompile Include="StdInc.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <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="CEmptyAI.h" />
+    <ClInclude Include="StdInc.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}</ProjectGuid>
+    <RootNamespace>EmptyAI</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)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <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>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" 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" />
+    <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.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.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.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(SolutionDir)\AI\</OutDir>
+    <IncludePath>$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <OutDir>$(SolutionDir)\AI\</OutDir>
+    <IncludePath>$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <OutDir>$(SolutionDir)$(Configuration)\bin\AI\</OutDir>
+    <IncludePath>$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
+    <OutDir>$(SolutionDir)$(Configuration)\bin\AI\</OutDir>
+    <IncludePath>$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <PreprocessorDefinitions>VCMI_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)..;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <OutputFile>$(OutDir)EmptyAI.dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <PreprocessorDefinitions>VCMI_DLL;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)..;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <OutputFile>$(OutDir)EmptyAI.dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <PreprocessorDefinitions>VCMI_DLL;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)..;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <OutputFile>$(OutDir)EmptyAI.dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <PreprocessorDefinitions>VCMI_DLL;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)..;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <OutputFile>$(OutDir)EmptyAI.dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 9 - 7
AI/VCAI/VCAI.cpp

@@ -464,13 +464,15 @@ void VCAI::gameOver(ui8 player, bool victory)
 			tlog0 << "VCAI: Player " << (int)player << " lost. It's me. What a disappointment! :(\n";
 		}
 
-		//let's make Impossible difficulty finally standing to its name :>
-		if(myCb->getStartInfo()->difficulty == 4 && !victory)
-		{
-			//play dirty: crash the whole engine to avoid lose
-			//that way AI is unbeatable!
-			*(int*)NULL = 666;
-		}
+// 		//let's make Impossible difficulty finally standing to its name :>
+// 		if(myCb->getStartInfo()->difficulty == 4 && !victory)
+// 		{
+// 			//play dirty: crash the whole engine to avoid lose
+// 			//that way AI is unbeatable!
+// 			*(int*)NULL = 666;
+// 		}
+
+// 		TODO - at least write some insults on stdout
 
 		finish();
 	}

+ 8 - 10
VCMI_VS10.sln

@@ -7,8 +7,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VCMI_lib", "lib\VCMI_lib.vc
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VCMI_server", "server\VCMI_server.vcxproj", "{8AF697C3-465E-4910-B31B-576A9ECDB309}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "genius", "AI\GeniusAI\genius.vcxproj", "{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}"
-EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StupidAI", "AI\StupidAI\StupidAI.vcxproj", "{15DABC90-234A-4B6B-9EEB-777C4768B82B}"
 	ProjectSection(ProjectDependencies) = postProject
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
@@ -19,6 +17,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ERM", "Scripting\ERM\ERM.vc
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F} = {B952FFC5-3039-4DE1-9F08-90ACDA483D8F}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VCAI", "AI\VCAI\VCAI.vcxproj", "{276C3DB0-7A6B-4417-8E5C-322B08633AAC}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -51,14 +51,6 @@ Global
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.RD|Win32.Build.0 = RD|Win32
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.RD|x64.ActiveCfg = RD|x64
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.RD|x64.Build.0 = RD|x64
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.Debug|Win32.ActiveCfg = Debug|Win32
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.Debug|Win32.Build.0 = Debug|Win32
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.Debug|x64.ActiveCfg = Debug|x64
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.Debug|x64.Build.0 = Debug|x64
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.RD|Win32.ActiveCfg = RD|Win32
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.RD|Win32.Build.0 = RD|Win32
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.RD|x64.ActiveCfg = RD|x64
-		{B6A14ED9-E7C1-411B-A596-2FE90B3145B4}.RD|x64.Build.0 = RD|x64
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|Win32.ActiveCfg = Debug|Win32
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|Win32.Build.0 = Debug|Win32
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|x64.ActiveCfg = Debug|x64
@@ -75,6 +67,12 @@ Global
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|Win32.Build.0 = RD|Win32
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|x64.ActiveCfg = RD|x64
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|x64.Build.0 = RD|x64
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|Win32.ActiveCfg = Debug|Win32
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|Win32.Build.0 = Debug|Win32
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|x64.ActiveCfg = Debug|Win32
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.RD|Win32.ActiveCfg = RD|Win32
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.RD|Win32.Build.0 = RD|Win32
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.RD|x64.ActiveCfg = RD|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 1
client/CMT.cpp

@@ -189,7 +189,7 @@ int main(int argc, char** argv)
 		("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
 		("start", po::value<std::string>(), "starts game from saved StartInfo file")
 		("onlyAI", "runs without GUI, all players will be default AI")
-		("oneGoodAI", "puts one default AI and the rest will be GeniusAI")
+		("oneGoodAI", "puts one default AI and the rest will be EmptyAI")
 		("autoSkip", "automatically skip turns in GUI")
 		("nointro,i", "skips intro movies");
 

+ 4 - 8
client/CPreGame.cpp

@@ -2582,11 +2582,9 @@ bool mapSorter::operator()(const CMapInfo *aaa, const CMapInfo *bbb)
 			return (a->victoryCondition.condition < b->victoryCondition.condition);
 			break;
 		case _name: //by name
-			return (a->name<b->name);
-			break;
+			return boost::ilexicographical_compare(a->name, b->name);
 		default:
-			return (a->name<b->name);
-			break;
+			return boost::ilexicographical_compare(a->name, b->name);
 		}
 	}
 	else //if we are sorting campaigns
@@ -2598,11 +2596,9 @@ bool mapSorter::operator()(const CMapInfo *aaa, const CMapInfo *bbb)
 					CGI->generaltexth->campaignRegionNames[ bbb->campaignHeader->mapVersion ].size();
 				break;
 			case _name: //by name
-				return aaa->campaignHeader->name < bbb->campaignHeader->name;
-				break;
+				return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name);
 			default:
-				return aaa->campaignHeader->name < bbb->campaignHeader->name;
-				break;
+				return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name);
 		}
 	}
 }

+ 1 - 1
client/Client.cpp

@@ -372,7 +372,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 			{
 				std::string AItoGive = settings["server"]["playerAI"].String();
 				if(!sensibleAILimit)
-					AItoGive = "GeniusAI";
+					AItoGive = "EmptyAI";
 				else
 					sensibleAILimit--;
 				playerint[color] = static_cast<CGameInterface*>(CDynLibHandler::getNewAI(AItoGive));

+ 50 - 31
lib/BattleState.cpp

@@ -933,15 +933,16 @@ int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstan
 	}
 	switch(spell->id)
 	{
-	case 56: //frenzy
+	case Spells::FRENZY:
 		return 1;
 	default: //other spells
-		return caster->getPrimSkillLevel(2) + caster->valOfBonuses(Bonus::SPELL_DURATION);
+		return caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + caster->valOfBonuses(Bonus::SPELL_DURATION);
 	}
 }
 
-CStack * BattleInfo::generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, BattleHex position) const
+CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerOwned, int slot, BattleHex position) const
 {
+	int stackID = getIdForNewStack();
 	int owner = attackerOwned ? sides[0] : sides[1];
 	assert((owner >= GameConstants::PLAYER_LIMIT)  ||
 		   (base.armyObj && base.armyObj->tempOwner == owner));
@@ -950,8 +951,9 @@ CStack * BattleInfo::generateNewStack(const CStackInstance &base, int stackID, b
 	ret->position = position;
 	return ret;
 }
-CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, BattleHex position) const
+CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, int slot, BattleHex position) const
 {
+	int stackID = getIdForNewStack();
 	int owner = attackerOwned ? sides[0] : sides[1];
 	CStack * ret = new CStack(&base, owner, stackID, attackerOwned, slot);
 	ret->position = position;
@@ -1425,25 +1427,28 @@ void BattleInfo::localInit()
 		b->attachTo(this);
 
 	BOOST_FOREACH(CStack *s, stacks)
-	{
-		s->exportBonuses();
-		if(s->base) //stack originating from "real" stack in garrison -> attach to it
-		{
-			s->attachTo(const_cast<CStackInstance*>(s->base));
-		}
-		else //attach directly to obj to which stack belongs and creature type
-		{
-			CArmedInstance *army = belligerents[!s->attackerOwned];
-			s->attachTo(army);
-			assert(s->type);
-			s->attachTo(const_cast<CCreature*>(s->type));
-		}
-		s->postInit();
-	}
+		localInitStack(s);
 
 	exportBonuses();
 }
 
+void BattleInfo::localInitStack(CStack * s)
+{
+	s->exportBonuses();
+	if(s->base) //stack originating from "real" stack in garrison -> attach to it
+	{
+		s->attachTo(const_cast<CStackInstance*>(s->base));
+	}
+	else //attach directly to obj to which stack belongs and creature type
+	{
+		CArmedInstance *army = belligerents[!s->attackerOwned];
+		s->attachTo(army);
+		assert(s->type);
+		s->attachTo(const_cast<CCreature*>(s->type));
+	}
+	s->postInit();
+}
+
 namespace CGH
 {
 	using namespace std;
@@ -1519,7 +1524,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 		else
 			pos = attackerLoose[armies[0]->stacksCount()-1][k];
 
-		CStack * stack = curB->generateNewStack(*i->second, stacks.size(), true, i->first, pos);
+		CStack * stack = curB->generateNewStack(*i->second, true, i->first, pos);
 		stacks.push_back(stack);
 	}
 
@@ -1534,7 +1539,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 		else
 			pos = defenderLoose[armies[1]->stacksCount()-1][k];
 
-		CStack * stack = curB->generateNewStack(*i->second, stacks.size(), false, i->first, pos);
+		CStack * stack = curB->generateNewStack(*i->second, false, i->first, pos);
 		stacks.push_back(stack);
 	}
 
@@ -1559,17 +1564,17 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 		{
 			if(heroes[0]->getArt(13)) //ballista
 			{
-				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1), stacks.size(), true, 255, 52);
+				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1), true, 255, 52);
 				stacks.push_back(stack);
 			}
 			if(heroes[0]->getArt(14)) //ammo cart
 			{
-				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), true, 255, 18);
+				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), true, 255, 18);
 				stacks.push_back(stack);
 			}
 			if(heroes[0]->getArt(15)) //first aid tent
 			{
-				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), true, 255, 154);
+				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), true, 255, 154);
 				stacks.push_back(stack);
 			}
 		}
@@ -1578,23 +1583,23 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 			//defending hero shouldn't receive ballista (bug #551)
 			if(heroes[1]->getArt(13) && !town) //ballista
 			{
-				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1),  stacks.size(), false, 255, 66);
+				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1),  false, 255, 66);
 				stacks.push_back(stack);
 			}
 			if(heroes[1]->getArt(14)) //ammo cart
 			{
-				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), false, 255, 32);
+				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), false, 255, 32);
 				stacks.push_back(stack);
 			}
 			if(heroes[1]->getArt(15)) //first aid tent
 			{
-				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), false, 255, 168);
+				CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), false, 255, 168);
 				stacks.push_back(stack);
 			}
 		}
 		if(town && heroes[0] && town->hasFort()) //catapult
 		{
-			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(145, 1), stacks.size(), true, 255, 120);
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(145, 1), true, 255, 120);
 			stacks.push_back(stack);
 		}
 	}
@@ -1603,15 +1608,15 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 	if (curB->siege == 2 || curB->siege == 3)
 	{
 		// keep tower
-		CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -2);
+		CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), false, 255, -2);
 		stacks.push_back(stack);
 
 		if (curB->siege == 3)
 		{
 			// lower tower + upper tower
-			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -4);
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), false, 255, -4);
 			stacks.push_back(stack);
-			stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -3);
+			stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), false, 255, -3);
 			stacks.push_back(stack);
 		}
 	}
@@ -2302,6 +2307,20 @@ ui8 BattleInfo::whatSide(int player) const
 	return -1;
 }
 
+int BattleInfo::getIdForNewStack() const
+{
+	if(stacks.size())
+	{
+		//stacks vector may be sorted not by ID and they may be not contiguous -> find stack with max ID
+		auto highestIDStack = *std::max_element(stacks.begin(), stacks.end(), 
+								[](const CStack *a, const CStack *b) { return a->ID < b->ID; });
+
+		return highestIDStack->ID + 1;
+	}
+
+	return 0;
+}
+
 CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
 	: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),   
 	counterAttacks(1)

+ 5 - 2
lib/BattleState.h

@@ -108,8 +108,9 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
 	std::set<BattleHex> getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
 	std::set<CStack*> getAdjacentCreatures (const CStack * stack) const;
 	static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
-	CStack * generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
-	CStack * generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack
 	ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
 	int hexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
 	int lineToWallHex(int line) const; //returns hex with wall in given line
@@ -143,6 +144,8 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
 	const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none
 	si8 battleMinSpellLevel() const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
 	void localInit();
+
+	void localInitStack(CStack * s);
 	static BattleInfo * setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
 	bool isInTacticRange( BattleHex dest ) const;
 	int getSurrenderingCost(int player) const;

+ 7 - 8
lib/NetPacksLib.cpp

@@ -1288,15 +1288,14 @@ DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs)
 		tlog2 << "No place found for new stack!\n";
 		return;
 	}
-	CStackInstance *csi = new CStackInstance(creID, amount);
-	csi->setArmyObj(gs->curB->belligerents[attacker ? 0 : 1]);
-	int newStackID = gs->curB->stacks.size() ? gs->curB->stacks.back()->ID+1 : 0; //stacks IDs may not be contiguous, so we need to give after-last ID
-	CStack * summonedStack = gs->curB->generateNewStack(*csi, newStackID, attacker, 255, pos); //TODO: netpacks?
+
+	CStackBasicDescriptor csbd(creID, amount);
+	CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, 255, pos); //TODO: netpacks?
 	if (summoned)
-		summonedStack->state.insert(EBattleStackState::SUMMONED);
-	summonedStack->attachTo(csi);
-	summonedStack->postInit();
-	gs->curB->stacks.push_back(summonedStack); //the stack is not "SUMMONED", it is permanent
+		addedStack->state.insert(EBattleStackState::SUMMONED);
+
+	gs->curB->localInitStack(addedStack);
+	gs->curB->stacks.push_back(addedStack); //the stack is not "SUMMONED", it is permanent
 }
 
 DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState *gs)