소스 검색

Merge tag '0.99'

# Conflicts:
#	debian/changelog
#	lib/GameConstants.h
AlexVinS 9 년 전
부모
커밋
258202d682
100개의 변경된 파일3333개의 추가작업 그리고 3077개의 파일을 삭제
  1. 8 0
      .gitignore
  2. 26 0
      .travis.linux
  3. 17 0
      .travis.osx
  4. 23 37
      .travis.yml
  5. 14 19
      AI/BattleAI/BattleAI.cbp
  6. 117 66
      AI/BattleAI/BattleAI.cpp
  7. 10 10
      AI/BattleAI/BattleAI.h
  8. 3 3
      AI/BattleAI/CMakeLists.txt
  9. 1 1
      AI/BattleAI/StdInc.cpp
  10. 2 2
      AI/BattleAI/main.cpp
  11. 9 3
      AI/CMakeLists.txt
  12. 2 3
      AI/EmptyAI/CEmptyAI.cpp
  13. 3 3
      AI/EmptyAI/CEmptyAI.h
  14. 3 3
      AI/EmptyAI/CMakeLists.txt
  15. 15 23
      AI/EmptyAI/EmptyAI.cbp
  16. 1 1
      AI/EmptyAI/StdInc.cpp
  17. 3 3
      AI/EmptyAI/exp_funcs.cpp
  18. 13 27
      AI/FuzzyLite/FuzzyLite.cbp
  19. 1 1
      AI/FuzzyLite/FuzzyLite.vcxproj
  20. 3 3
      AI/StupidAI/CMakeLists.txt
  21. 1 1
      AI/StupidAI/StdInc.cpp
  22. 6 28
      AI/StupidAI/StupidAI.cbp
  23. 14 8
      AI/StupidAI/StupidAI.cpp
  24. 4 4
      AI/StupidAI/StupidAI.h
  25. 2 2
      AI/StupidAI/main.cpp
  26. 17 11
      AI/VCAI/AIUtility.cpp
  27. 8 9
      AI/VCAI/AIUtility.h
  28. 9 8
      AI/VCAI/CMakeLists.txt
  29. 18 17
      AI/VCAI/Fuzzy.cpp
  30. 4 1
      AI/VCAI/Fuzzy.h
  31. 72 42
      AI/VCAI/Goals.cpp
  32. 10 7
      AI/VCAI/Goals.h
  33. 17 26
      AI/VCAI/VCAI.cbp
  34. 269 163
      AI/VCAI/VCAI.cpp
  35. 21 29
      AI/VCAI/VCAI.h
  36. 2 2
      AI/VCAI/main.cpp
  37. 5 2
      AUTHORS
  38. 13 22
      CCallback.cpp
  39. 25 26
      CCallback.h
  40. 29 27
      CMakeLists.txt
  41. 27 2
      ChangeLog
  42. 38 18
      Global.h
  43. 7 0
      Mods/vcmi/Sprites/ScSelC.json
  44. BIN
      Mods/vcmi/Sprites/mapFormatIcons/vcmi1.png
  45. 19 2
      README.linux
  46. 2 1
      README.md
  47. 1 1
      VCMI_global.props
  48. 6 0
      Version.cpp.in
  49. 4 0
      Version.h
  50. 16 8
      client/CBitmapHandler.cpp
  51. 1 4
      client/CBitmapHandler.h
  52. 5 29
      client/CDefHandler.cpp
  53. 4 4
      client/CDefHandler.h
  54. 293 253
      client/CMT.cpp
  55. 1 4
      client/CMT.h
  56. 6 4
      client/CMakeLists.txt
  57. 1 2
      client/CMessage.cpp
  58. 23 41
      client/CMusicHandler.cpp
  59. 12 13
      client/CMusicHandler.h
  60. 187 169
      client/CPlayerInterface.cpp
  61. 21 29
      client/CPlayerInterface.h
  62. 139 119
      client/CPreGame.cpp
  63. 48 45
      client/CPreGame.h
  64. 11 76
      client/CVideoHandler.cpp
  65. 28 20
      client/CVideoHandler.h
  66. 204 164
      client/Client.cpp
  67. 32 33
      client/Client.h
  68. 55 33
      client/Graphics.cpp
  69. 18 15
      client/Graphics.h
  70. 125 116
      client/NetPacksClient.cpp
  71. 95 0
      client/SDLRWwrapper.cpp
  72. 16 0
      client/SDLRWwrapper.h
  73. 27 49
      client/VCMI_client.cbp
  74. 2 0
      client/VCMI_client.vcxproj
  75. 10 6
      client/battle/CBattleAnimations.cpp
  76. 25 25
      client/battle/CBattleAnimations.h
  77. 190 322
      client/battle/CBattleInterface.cpp
  78. 119 93
      client/battle/CBattleInterface.h
  79. 77 7
      client/battle/CBattleInterfaceClasses.cpp
  80. 20 11
      client/battle/CBattleInterfaceClasses.h
  81. 8 23
      client/battle/CCreatureAnimation.cpp
  82. 1 1
      client/battle/CCreatureAnimation.h
  83. 95 129
      client/gui/CAnimation.cpp
  84. 15 15
      client/gui/CAnimation.h
  85. 0 2
      client/gui/CCursorHandler.cpp
  86. 59 83
      client/gui/CGuiHandler.cpp
  87. 12 11
      client/gui/CGuiHandler.h
  88. 7 21
      client/gui/CIntObject.h
  89. 0 7
      client/gui/Geometries.h
  90. 0 11
      client/gui/SDL_Compat.h
  91. 22 54
      client/gui/SDL_Extensions.cpp
  92. 1 51
      client/gui/SDL_Extensions.h
  93. 0 8
      client/gui/SDL_Pixels.h
  94. 123 76
      client/mapHandler.cpp
  95. 19 17
      client/mapHandler.h
  96. 15 41
      client/widgets/AdventureMapClasses.cpp
  97. 35 34
      client/widgets/AdventureMapClasses.h
  98. 81 25
      client/widgets/Buttons.cpp
  99. 39 20
      client/widgets/Buttons.h
  100. 66 87
      client/widgets/CArtifactHolder.cpp

+ 8 - 0
.gitignore

@@ -13,6 +13,9 @@
 *.layout
 *.pro.user
 *.pro.user.*
+*.swp
+*.h.gch
+*~
 /CMakeLists.txt.user
 CMakeCache.txt
 CMakeFiles
@@ -28,3 +31,8 @@ ui_*.h
 /CPackSourceConfig.cmake
 build-*
 CMakeLists.txt.user.*
+Doxyfile
+doc/*
+VCMI_VS11.sdf
+*.ipch
+VCMI_VS11.opensdf

+ 26 - 0
.travis.linux

@@ -0,0 +1,26 @@
+#!/bin/sh
+
+#new Clang
+sudo add-apt-repository --yes ppa:h-rayflood/llvm
+
+#new SDL2
+sudo add-apt-repository --yes ppa:zoogie/sdl2-snapshots
+
+#new Qt
+sudo add-apt-repository --yes ppa:beineri/opt-qt57-trusty
+
+#new CMake
+sudo add-apt-repository --yes ppa:george-edison55/cmake-3.x
+
+sudo apt-get update -qq
+
+sudo apt-get install -qq $SUPPORT
+sudo apt-get install -qq $PACKAGE
+sudo apt-get install -qq cmake libboost1.54-all-dev zlib1g-dev
+sudo apt-get install -qq libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
+sudo apt-get install -qq libavformat-dev libswscale-dev
+sudo apt-get install -qq qt57declarative
+
+#setup compiler
+source /opt/qt57/bin/qt57-env.sh
+export CC=${REAL_CC} CXX=${REAL_CXX}

+ 17 - 0
.travis.osx

@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# https://gist.github.com/vmarkovtsev/3f2dd98e56cb63098027
+
+brew unlink boost
+brew install boost smpeg2 innoextract libpng freetype sdl2 sdl2_ttf sdl2_image qt5 ffmpeg
+brew install sdl2_mixer --with-smpeg2
+sudo sed -i.bak 's/106200/106900/g' /usr/local/Cellar/cmake/*/share/cmake/Modules/FindBoost.cmake
+
+export CMAKE_PREFIX_PATH="/usr/local/opt/qt5:$CMAKE_PREFIX_PATH"
+
+sparkle_version=$(curl -L https://github.com/sparkle-project/Sparkle/releases/latest | grep '<span class="css-truncate-target">' | sed -E 's|^.*([1-9]\.[0-9]+\.[0-9]+).*$|\1|g')
+wget https://github.com/sparkle-project/Sparkle/releases/download/$sparkle_version/Sparkle-$sparkle_version.tar.bz2
+mkdir sparkle && cd sparkle
+tar -xf ../Sparkle-*.tar.bz2
+sudo mv Sparkle.framework /Library/Frameworks/
+cd .. && rm -rf sparkle

+ 23 - 37
.travis.yml

@@ -1,39 +1,22 @@
 language: cpp
+os:
+  - linux
+  - osx
+dist: trusty
+sudo: required
 
 before_install:
-  #new boost
-  - sudo add-apt-repository --yes ppa:boost-latest/ppa
-  #new GCC
-  - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
-  #new Clang
-  - sudo add-apt-repository --yes ppa:h-rayflood/llvm
-  #new SDL2
-  - sudo add-apt-repository --yes ppa:zoogie/sdl2-snapshots
-  #new Qt
-  - sudo add-apt-repository --yes ppa:beineri/opt-qt532
-  #new CMake
-  - sudo add-apt-repository --yes ppa:andykimpe/cmake 
-
-  - sudo apt-get update -qq
-
-  - sudo apt-get install -qq $SUPPORT
-  - sudo apt-get install -qq $PACKAGE
-  - sudo apt-get install -qq cmake libboost1.55-all-dev zlib1g-dev
-  - sudo apt-get install -qq libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
-  - sudo apt-get install -qq libavformat-dev libswscale-dev
-  - sudo apt-get install -qq qt53declarative
-
-  #setup compiler
-  - source /opt/qt53/bin/qt53-env.sh
-  - export CC=${REAL_CC} CXX=${REAL_CXX}
+  - if [[ $TRAVIS_OS_NAME == 'linux' ]]; then . .travis.linux; fi
+  - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then . .travis.osx; fi
 
 before_script:
   - mkdir build
   - cd build
-  - cmake ..
+  - cmake -G "Unix Makefiles" ..
 
 script:
-  - make
+  - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then cd ..; xcodebuild -project osx/osx-vcmibuilder/vcmibuilder.xcodeproj/ -configuration Release CONFIGURATION_BUILD_DIR=..; cd build; fi
+  - make -j2
 
 env:
   - ignore=this
@@ -42,16 +25,19 @@ matrix:
   exclude:
     - env: ignore=this
   include:
-    - compiler: clang # fails all the time - missing packages?
-      env: REAL_CC=clang-3.2 REAL_CXX=clang++-3.2 PACKAGE=clang-3.2 SUPPORT=g++-4.8 
-    - compiler: clang
-      env: REAL_CC=clang-3.3 REAL_CXX=clang++-3.3 PACKAGE=clang-3.3 SUPPORT=g++-4.8 
-    - compiler: clang
-      env: REAL_CC=clang-3.4 REAL_CXX=clang++-3.4 PACKAGE=clang-3.4 SUPPORT=g++-4.8 
-    #- compiler: gcc # fails due to running out of memory - vcmi need too much of it for successfull compilation
-    #  env: REAL_CC=gcc-4.7   REAL_CXX=g++-4.7     PACKAGE=g++-4.7   SUPPORT=
-    #- compiler: gcc # same as 4.7
-    #  env: REAL_CC=gcc-4.8   REAL_CXX=g++-4.8     PACKAGE=g++-4.8   SUPPORT=
+    - os: linux
+      compiler: clang
+      env: REAL_CC=clang-3.4 REAL_CXX=clang++-3.4 PACKAGE=clang-3.4 SUPPORT=libstdc++-4.8-dev
+    - os: linux
+      compiler: clang
+      env: REAL_CC=clang-3.5 REAL_CXX=clang++-3.5 PACKAGE=clang-3.5 SUPPORT=libstdc++-4.8-dev
+    - os: linux
+      compiler: clang
+      env: REAL_CC=clang-3.6 REAL_CXX=clang++-3.6 PACKAGE=clang-3.6 SUPPORT=libstdc++-4.8-dev
+    - os: linux
+      compiler: gcc
+      env: REAL_CC=gcc-4.8   REAL_CXX=g++-4.8     PACKAGE=g++-4.8   SUPPORT=
+    - os: osx
 
 notifications:
   email:

+ 14 - 19
AI/BattleAI/BattleAI.cbp

@@ -6,20 +6,23 @@
 		<Option pch_mode="2" />
 		<Option compiler="gcc" />
 		<Build>
-			<Target title="Debug-win32-SDL2">
+			<Target title="Debug-win32">
 				<Option platforms="Windows;" />
 				<Option output="../BattleAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x86/" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
 					<Add option="-g" />
 				</Compiler>
+				<Linker>
+					<Add directory="$(#boost.lib32)" />
+				</Linker>
 			</Target>
-			<Target title="Release-win32-SDL2">
+			<Target title="Release-win32">
 				<Option platforms="Windows;" />
 				<Option output="../BattleAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
+				<Option object_output="obj/Release/x86/" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
@@ -27,29 +30,20 @@
 				</Compiler>
 				<Linker>
 					<Add option="-s" />
+					<Add directory="$(#boost.lib32)" />
 				</Linker>
 			</Target>
-			<Target title="Debug-win32-SDL1">
+			<Target title="Debug-win64">
 				<Option platforms="Windows;" />
 				<Option output="../BattleAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x64/" />
 				<Option type="3" />
-				<Option compiler="gcc" />
+				<Option compiler="gnu_gcc_compiler_x64" />
 				<Compiler>
 					<Add option="-g" />
 				</Compiler>
-			</Target>
-			<Target title="Release-win32-SDL1">
-				<Option platforms="Windows;" />
-				<Option output="../BattleAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
-				<Option type="3" />
-				<Option compiler="gcc" />
-				<Compiler>
-					<Add option="-O2" />
-				</Compiler>
 				<Linker>
-					<Add option="-s" />
+					<Add directory="$(#boost.lib64)" />
 				</Linker>
 			</Target>
 		</Build>
@@ -57,6 +51,7 @@
 			<Add option="-pedantic" />
 			<Add option="-Wextra" />
 			<Add option="-Wall" />
+			<Add option="-std=gnu++11" />
 			<Add option="-fexceptions" />
 			<Add option="-Wpointer-arith" />
 			<Add option="-Wno-switch" />
@@ -69,12 +64,12 @@
 		<Linker>
 			<Add option="-lboost_system$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib32)" />
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="BattleAI.cpp" />
 		<Unit filename="BattleAI.h" />
 		<Unit filename="StdInc.h">
+			<Option compile="1" />
 			<Option weight="0" />
 		</Unit>
 		<Unit filename="main.cpp" />

+ 117 - 66
AI/BattleAI/BattleAI.cpp

@@ -8,7 +8,7 @@
 #include "../../lib/VCMI_Lib.h"
 
 using boost::optional;
-static shared_ptr<CBattleCallback> cbc;
+static std::shared_ptr<CBattleCallback> cbc;
 
 #define LOGL(text) print(text)
 #define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
@@ -91,7 +91,7 @@ CBattleAI::~CBattleAI(void)
 	}
 }
 
-void CBattleAI::init(shared_ptr<CBattleCallback> CB)
+void CBattleAI::init(std::shared_ptr<CBattleCallback> CB)
 {
 	print("init called, saving ptr to IBattleCallback");
 	cbc = cb = CB;
@@ -103,11 +103,6 @@ void CBattleAI::init(shared_ptr<CBattleCallback> CB)
 	CB->unlockGsWhenWaiting = false;
 }
 
-static bool thereRemainsEnemy()
-{
-	return !cbc->battleIsFinished();
-}
-
 BattleAction CBattleAI::activeStack( const CStack * stack )
 {
 	LOG_TRACE_PARAMS(logAi, "stack: %s", stack->nodeName())	;
@@ -136,21 +131,20 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 		if(cb->battleCanCastSpell())
 			attemptCastingSpell();
 
-		if(!thereRemainsEnemy())
-			return BattleAction();
+		if(auto ret = cbc->battleIsFinished())
+		{
+			//spellcast may finish battle
+			//send special preudo-action
+			BattleAction cancel;
+			cancel.actionType = Battle::CANCEL;
+			return cancel;
+		}
 
 		if(auto action = considerFleeingOrSurrendering())
 			return *action;
 
-		if(cb->battleGetStacks(CBattleInfoEssentials::ONLY_ENEMY).empty())
-		{
-			//We apparently won battle by casting spell, return defend... (accessing cb may cause trouble)
-			return BattleAction::makeDefend(stack);
-		}
-
 		PotentialTargets targets(stack);
 
-
 		if(targets.possibleAttacks.size())
 		{
 			auto hlp = targets.bestAction();
@@ -165,7 +159,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 			{
 				ThreatMap threatsToUs(stack);
 				auto dists = cbc->battleGetDistances(stack);
-                const EnemyInfo &ei= *range::min_element(targets.unreachableEnemies, std::bind(isCloser, _1, _2, std::ref(dists)));
+				const EnemyInfo &ei= *range::min_element(targets.unreachableEnemies, std::bind(isCloser, _1, _2, std::ref(dists)));
 				if(distToNearestNeighbour(ei.s->position, dists) < GameConstants::BFIELD_SIZE)
 				{
 					return goTowards(stack, ei.s->position);
@@ -179,7 +173,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 	}
 	catch(std::exception &e)
 	{
-		logAi->errorStream() << "Exception occurred in " << __FUNCTION__ << " " << e.what();
+		logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what());
 	}
 
 	return BattleAction::makeDefend(stack);
@@ -268,7 +262,7 @@ void CBattleAI::battleStacksRemoved(const BattleStacksRemoved & bsr)
 
 void CBattleAI::print(const std::string &text) const
 {
-    logAi->traceStream() << "CBattleAI [" << this <<"]: " << text;
+	logAi->trace("CBattleAI [%p]: %s", this, text);
 }
 
 BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
@@ -283,7 +277,7 @@ BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
 	auto destNeighbours = destination.neighbouringTiles();
 	if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
 	{
-        logAi->warnStream() << "Warning: already standing on neighbouring tile!";
+		logAi->warn("Warning: already standing on neighbouring tile!");
 		//We shouldn't even be here...
 		return BattleAction::makeDefend(stack);
 	}
@@ -358,6 +352,7 @@ struct PossibleSpellcast
 {
 	const CSpell *spell;
 	BattleHex dest;
+	si32 value;
 };
 
 struct CurrentOffensivePotential
@@ -430,9 +425,9 @@ void CBattleAI::attemptCastingSpell()
 	std::vector<PossibleSpellcast> possibleCasts;
 	for(auto spell : possibleSpells)
 	{
-		for(auto hex : getTargetsToConsider(spell))
+		for(auto hex : getTargetsToConsider(spell, hero))
 		{
-			PossibleSpellcast ps = {spell, hex};
+			PossibleSpellcast ps = {spell, hex, 0};
 			possibleCasts.push_back(ps);
 		}
 	}
@@ -457,8 +452,8 @@ void CBattleAI::attemptCastingSpell()
 		case OFFENSIVE_SPELL:
 			{
 				int damageDealt = 0, damageReceived = 0;
-				
-				auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, playerID, skillLevel, ps.dest, hero);
+
+				auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, hero, skillLevel, ps.dest);
 
 				if(stacksSuffering.empty())
 					return -1;
@@ -472,41 +467,52 @@ void CBattleAI::attemptCastingSpell()
 						damageDealt += dmg;
 				}
 
-				const int damageDiff = damageDealt - damageReceived;
+				const int damageDiff = damageDealt - damageReceived * 10;
 
-
-				LOGFL("Casting %s on hex %d would deal %d damage points among %d stacks.",
-					ps.spell->name % ps.dest % damageDiff % stacksSuffering.size());
+				LOGFL("Casting %s on hex %d would deal { %d %d } damage points among %d stacks.",
+					ps.spell->name % ps.dest % damageDealt % damageReceived % stacksSuffering.size());
 				//TODO tactic effect too
 				return damageDiff;
 			}
 		case TIMED_EFFECT:
 			{
-				StackWithBonuses swb;
-				swb.stack = cb->battleGetStackByPos(ps.dest);
-				if(!swb.stack)
+				auto stacksAffected = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, hero, skillLevel, ps.dest);
+
+				if(stacksAffected.empty())
 					return -1;
 
-				Bonus pseudoBonus;
-				pseudoBonus.sid = ps.spell->id;
-				pseudoBonus.val = skillLevel;
-				pseudoBonus.turnsRemain = 1; //TODO
-				CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus);
+				int totalGain = 0;
+
+				for(const CStack * sta : stacksAffected)
+				{
+					StackWithBonuses swb;
+					swb.stack = sta;
+
+					Bonus pseudoBonus;
+					pseudoBonus.sid = ps.spell->id;
+					pseudoBonus.val = skillLevel;
+					pseudoBonus.turnsRemain = 1; //TODO
+					CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus);
 
-				HypotheticChangesToBattleState state;
-				state.bonusesOfStacks[swb.stack] = &swb;
+					HypotheticChangesToBattleState state;
+					state.bonusesOfStacks[swb.stack] = &swb;
 
-				PotentialTargets pt(swb.stack, state);
-				auto newValue = pt.bestActionValue();
-				auto oldValue = valueOfStack[swb.stack];
-				auto gain = newValue - oldValue;
-				if(swb.stack->owner != playerID) //enemy
-					gain = -gain;
+					PotentialTargets pt(swb.stack, state);
+					auto newValue = pt.bestActionValue();
+					auto oldValue = valueOfStack[swb.stack];
+					auto gain = newValue - oldValue;
+					if(swb.stack->owner != playerID) //enemy
+						gain = -gain;
 
-				LOGFL("Casting %s on %s would improve the stack by %d points (from %d to %d)",
-					ps.spell->name % swb.stack->nodeName() % gain % (oldValue) % (newValue));
+					LOGFL("Casting %s on %s would improve the stack by %d points (from %d to %d)",
+						ps.spell->name % sta->nodeName() % (gain) % (oldValue) % (newValue));
 
-				return gain;
+					totalGain += gain;
+				}
+
+				LOGFL("Total gain of cast %s at hex %d is %d", ps.spell->name % (ps.dest.hex) % (totalGain));
+
+				return totalGain;
 			}
 		default:
 			assert(0);
@@ -514,7 +520,15 @@ void CBattleAI::attemptCastingSpell()
 		}
 	};
 
-	auto castToPerform = *vstd::maxElementByFun(possibleCasts, evaluateSpellcast);
+	for(PossibleSpellcast & psc : possibleCasts)
+		psc.value = evaluateSpellcast(psc);
+
+	auto pscValue = [] (const PossibleSpellcast &ps) -> int
+	{
+		return ps.value;
+	};
+
+	auto castToPerform = *vstd::maxElementByFun(possibleCasts, pscValue);
 	LOGFL("Best spell is %s. Will cast.", castToPerform.spell->name);
 
 	BattleAction spellcast;
@@ -527,24 +541,60 @@ void CBattleAI::attemptCastingSpell()
 	cb->battleMakeAction(&spellcast);
 }
 
-std::vector<BattleHex> CBattleAI::getTargetsToConsider( const CSpell *spell ) const
+std::vector<BattleHex> CBattleAI::getTargetsToConsider(const CSpell * spell, const ISpellCaster * caster) const
 {
-	if(spell->getTargetType() == CSpell::NO_TARGET)
-	{
-		//Spell can be casted anywhere, all hexes are potentially considerable.
-		std::vector<BattleHex> ret;
+	const CSpell::TargetInfo targetInfo(spell, caster->getSpellSchoolLevel(spell));
+	std::vector<BattleHex> ret;
 
-		for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
-			if(BattleHex(i).isAvailable())
-				ret.push_back(i);
-
-		return ret;
+	if(targetInfo.massive || targetInfo.type == CSpell::NO_TARGET)
+	{
+		ret.push_back(BattleHex());
 	}
 	else
 	{
-		//TODO when massive effect -> doesn't matter where cast
-		return cbc->battleGetPossibleTargets(playerID, spell);
+		switch(targetInfo.type)
+		{
+		case CSpell::CREATURE:
+			{
+				for(const CStack * stack : cbc->battleAliveStacks())
+				{
+					bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack);
+					bool casterStack = stack->owner == caster->getOwner();
+
+					if(!immune)
+						switch (spell->positiveness)
+						{
+						case CSpell::POSITIVE:
+							if(casterStack || targetInfo.smart)
+								ret.push_back(stack->position);
+							break;
+
+						case CSpell::NEUTRAL:
+							ret.push_back(stack->position);
+							break;
+
+						case CSpell::NEGATIVE:
+							if(!casterStack || targetInfo.smart)
+								ret.push_back(stack->position);
+							break;
+						}
+				}
+			}
+			break;
+		case CSpell::LOCATION:
+			{
+				for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
+					if(BattleHex(i).isAvailable())
+						ret.push_back(i);
+			}
+			break;
+
+		default:
+			break;
+		}
 	}
+
+	return ret;
 }
 
 boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
@@ -610,13 +660,14 @@ ThreatMap::ThreatMap(const CStack *Endangered) : endangered(Endangered)
 
 const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= nullptr*/, const std::string &cachingStr /*= ""*/) const
 {
-	TBonusListPtr ret = make_shared<BonusList>();
+	TBonusListPtr ret = std::make_shared<BonusList>();
 	const TBonusListPtr originalList = stack->getAllBonuses(selector, limit, root, cachingStr);
 	range::copy(*originalList, std::back_inserter(*ret));
 	for(auto &bonus : bonusesToAdd)
 	{
-		if(selector(&bonus)  &&  (!limit || !limit(&bonus)))
-			ret->push_back(&bonus);
+		auto b = std::make_shared<Bonus>(bonus);
+		if(selector(b.get())  &&  (!limit || !limit(b.get())))
+			ret->push_back(b);
 	}
 
 	//TODO limiters?
@@ -643,7 +694,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo
 	auto attacker = AttackInfo.attacker;
 	auto enemy = AttackInfo.defender;
 
-	const int remainingCounterAttacks = getValOr(state.counterAttacksLeft, enemy, enemy->counterAttacks);
+	const int remainingCounterAttacks = getValOr(state.counterAttacksLeft, enemy, enemy->counterAttacksRemaining());
 	const bool counterAttacksBlocked = attacker->hasBonusOfType(Bonus::BLOCKS_RETALIATION) || enemy->hasBonusOfType(Bonus::NO_RETALIATION);
 	const int totalAttacks = 1 + AttackInfo.attackerBonuses->getBonuses(Selector::type(Bonus::ADDITIONAL_ATTACK), (Selector::effectRange (Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_MELEE_FIGHT))))->totalValue();
 
@@ -653,7 +704,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo
 	for(int i  = 0; i < totalAttacks; i++)
 	{
 		std::pair<ui32, ui32> retaliation(0,0);
-		auto attackDmg = cbc->battleEstimateDamage(curBai, &retaliation);
+		auto attackDmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), curBai, &retaliation);
 		ap.damageDealt = (attackDmg.first + attackDmg.second) / 2;
 		ap.damageReceived = (retaliation.first + retaliation.second) / 2;
 
@@ -737,7 +788,7 @@ int PotentialTargets::bestActionValue() const
 
 void EnemyInfo::calcDmg(const CStack * ourStack)
 {
-	TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
+	TDmgRange retal, dmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal);
 	adi = (dmg.first + dmg.second) / 2;
 	adr = (retal.first + retal.second) / 2;
 }

+ 10 - 10
AI/BattleAI/BattleAI.h

@@ -51,11 +51,11 @@ static bool willSecondHexBlockMoreEnemyShooters(const BattleHex &h1, const Battl
 
 
 struct ThreatMap
-{	
+{
 	std::array<std::vector<BattleAttackInfo>, GameConstants::BFIELD_SIZE> threatMap; // [hexNr] -> enemies able to strike
-	
-	const CStack *endangered; 
-	std::array<int, GameConstants::BFIELD_SIZE> sufferedDamage; 
+
+	const CStack *endangered;
+	std::array<int, GameConstants::BFIELD_SIZE> sufferedDamage;
 
 	ThreatMap(const CStack *Endangered);
 };
@@ -89,7 +89,7 @@ const Val getValOr(const std::map<Key, Val> &Map, const Key &key, const Val2 def
 	auto i = Map.find(key);
 	if(i != Map.end())
 		return i->second;
-	else 
+	else
 		return defaultValue;
 }
 
@@ -110,9 +110,9 @@ struct PotentialTargets
 class CBattleAI : public CBattleGameInterface
 {
 	int side;
-	shared_ptr<CBattleCallback> cb;
-	
-	//Previous setting of cb 
+	std::shared_ptr<CBattleCallback> cb;
+
+	//Previous setting of cb
 	bool wasWaitingForRealize, wasUnlockingGs;
 
 	void print(const std::string &text) const;
@@ -120,7 +120,7 @@ public:
 	CBattleAI(void);
 	~CBattleAI(void);
 
-	void init(shared_ptr<CBattleCallback> CB) override;
+	void init(std::shared_ptr<CBattleCallback> CB) override;
 	void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
 	void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
 	BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
@@ -148,6 +148,6 @@ public:
 	boost::optional<BattleAction> considerFleeingOrSurrendering();
 
 	void attemptCastingSpell();
-	std::vector<BattleHex> getTargetsToConsider(const CSpell *spell) const;
+	std::vector<BattleHex> getTargetsToConsider(const CSpell *spell, const ISpellCaster * caster) const;
 };
 

+ 3 - 3
AI/BattleAI/CMakeLists.txt

@@ -1,12 +1,12 @@
 project(battleAI)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 
 set(battleAI_SRCS
 		StdInc.cpp
-        BattleAI.cpp
-        main.cpp
+		BattleAI.cpp
+		main.cpp
 )
 
 add_library(BattleAI SHARED ${battleAI_SRCS})

+ 1 - 1
AI/BattleAI/StdInc.cpp

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

+ 2 - 2
AI/BattleAI/main.cpp

@@ -25,7 +25,7 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 	strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
 }
 
-extern "C" DLL_EXPORT void GetNewBattleAI(shared_ptr<CBattleGameInterface> &out)
+extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
 {
-	out = make_shared<CBattleAI>();
+	out = std::make_shared<CBattleAI>();
 }

+ 9 - 3
AI/CMakeLists.txt

@@ -1,9 +1,15 @@
 project(AI)
 cmake_minimum_required(VERSION 2.8)
 
-find_package(Fuzzylite)
+option(FORCE_BUNDLED_FL "Force to use FuzzyLite included into VCMI's source tree" OFF)
 
-if(NOT MSVC)
+if (NOT FORCE_BUNDLED_FL)
+	find_package(Fuzzylite)
+else()
+	set(FL_FOUND FALSE)
+endif()
+
+if (NOT MSVC)
     add_definitions(-DFL_CPP11)
     set(FL_CPP11 ON CACHE BOOL "")
 endif()
@@ -16,4 +22,4 @@ endif()
 add_subdirectory(BattleAI)
 add_subdirectory(StupidAI)
 add_subdirectory(EmptyAI)
-add_subdirectory(VCAI)
+add_subdirectory(VCAI)

+ 2 - 3
AI/EmptyAI/CEmptyAI.cpp

@@ -3,12 +3,11 @@
 
 #include "../../lib/CRandomGenerator.h"
 
-void CEmptyAI::init(shared_ptr<CCallback> CB)
+void CEmptyAI::init(std::shared_ptr<CCallback> CB)
 {
 	cb = CB;
 	human=false;
 	playerID = *cb->getMyColor();
-	//logAi->infoStream() << "EmptyAI initialized.";
 }
 void CEmptyAI::yourTurn()
 {
@@ -30,7 +29,7 @@ void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Com
 	cb->selectionMade(0, askID);
 }
 
-void CEmptyAI::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
+void CEmptyAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
 {
 	cb->selectionMade(0, askID);
 }

+ 3 - 3
AI/EmptyAI/CEmptyAI.h

@@ -7,15 +7,15 @@ struct HeroMoveDetails;
 
 class CEmptyAI : public CGlobalAI
 {
-	shared_ptr<CCallback> cb;
+	std::shared_ptr<CCallback> cb;
 
 public:
-	void init(shared_ptr<CCallback> CB) override;
+	void init(std::shared_ptr<CCallback> CB) override;
 	void yourTurn() override;
 	void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
 	void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override;
-	void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
+	void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
 };
 

+ 3 - 3
AI/EmptyAI/CMakeLists.txt

@@ -1,11 +1,11 @@
 project(emptyAI)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 
 set(emptyAI_SRCS
-        CEmptyAI.cpp
-        exp_funcs.cpp
+    CEmptyAI.cpp
+    exp_funcs.cpp
 )
 
 add_library(EmptyAI SHARED ${emptyAI_SRCS})

+ 15 - 23
AI/EmptyAI/EmptyAI.cbp

@@ -6,58 +6,50 @@
 		<Option pch_mode="2" />
 		<Option compiler="gcc" />
 		<Build>
-			<Target title="Debug-win32-SDL2">
+			<Target title="Debug-win32">
 				<Option platforms="Windows;" />
 				<Option output="../EmptyAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x86/" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
 					<Add option="-ggdb" />
 				</Compiler>
+				<Linker>
+					<Add directory="$(#boost.lib32)" />
+				</Linker>
 			</Target>
-			<Target title="Release-win32-SDL2">
+			<Target title="Release-win32">
 				<Option platforms="Windows;" />
 				<Option output="../EmptyAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
+				<Option object_output="obj/Release/x86/" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
-					<Add option="-fomit-frame-pointer" />
-					<Add option="-O3" />
+					<Add option="-O2" />
 				</Compiler>
 				<Linker>
-					<Add option="-s" />
+					<Add directory="$(#boost.lib32)" />
 				</Linker>
 			</Target>
-			<Target title="Debug-win32-SDL1">
+			<Target title="Debug-win64">
 				<Option platforms="Windows;" />
 				<Option output="../EmptyAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x64/" />
 				<Option type="3" />
-				<Option compiler="gcc" />
+				<Option compiler="gnu_gcc_compiler_x64" />
 				<Compiler>
 					<Add option="-ggdb" />
 				</Compiler>
-			</Target>
-			<Target title="Release-win32-SDL1">
-				<Option platforms="Windows;" />
-				<Option output="../EmptyAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
-				<Option type="3" />
-				<Option compiler="gcc" />
-				<Compiler>
-					<Add option="-fomit-frame-pointer" />
-					<Add option="-O3" />
-				</Compiler>
 				<Linker>
-					<Add option="-s" />
+					<Add directory="$(#boost.lib64)" />
 				</Linker>
 			</Target>
 		</Build>
 		<Compiler>
 			<Add option="-Wextra" />
 			<Add option="-Wall" />
+			<Add option="-std=gnu++11" />
 			<Add option="-fexceptions" />
 			<Add option="-Wpointer-arith" />
 			<Add option="-Wno-switch" />
@@ -71,12 +63,12 @@
 		<Linker>
 			<Add option="-lboost_system$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib32)" />
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="CEmptyAI.cpp" />
 		<Unit filename="CEmptyAI.h" />
 		<Unit filename="StdInc.h">
+			<Option compile="1" />
 			<Option weight="0" />
 		</Unit>
 		<Unit filename="exp_funcs.cpp" />

+ 1 - 1
AI/EmptyAI/StdInc.cpp

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

+ 3 - 3
AI/EmptyAI/exp_funcs.cpp

@@ -13,7 +13,7 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 	strcpy(name,NAME);
 }
 
-extern "C" DLL_EXPORT void GetNewAI(shared_ptr<CGlobalAI> &out)
+extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out)
 {
-	out = make_shared<CEmptyAI>();
-}
+	out = std::make_shared<CEmptyAI>();
+}

+ 13 - 27
AI/FuzzyLite/FuzzyLite.cbp

@@ -6,23 +6,24 @@
 		<Option pch_mode="2" />
 		<Option compiler="gcc" />
 		<Build>
-			<Target title="Debug-win32-SDL2">
+			<Target title="Debug-win32">
 				<Option platforms="Windows;" />
-				<Option output="bin/Debug/FuzzyLite" prefix_auto="1" extension_auto="1" />
+				<Option output="../FuzzyLite" prefix_auto="1" extension_auto="1" />
 				<Option working_dir="" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x86/" />
 				<Option type="2" />
 				<Option compiler="gcc" />
 				<Option createDefFile="1" />
 				<Compiler>
+					<Add option="-Og" />
 					<Add option="-g" />
 				</Compiler>
 			</Target>
-			<Target title="Release-win32-SDL2">
+			<Target title="Release-win32">
 				<Option platforms="Windows;" />
-				<Option output="bin/Release/FuzzyLite" prefix_auto="1" extension_auto="1" />
+				<Option output="../FuzzyLite" prefix_auto="1" extension_auto="1" />
 				<Option working_dir="" />
-				<Option object_output="obj/Release/" />
+				<Option object_output="obj/Release/x86/" />
 				<Option type="2" />
 				<Option compiler="gcc" />
 				<Option createDefFile="1" />
@@ -34,38 +35,24 @@
 					<Add option="-s" />
 				</Linker>
 			</Target>
-			<Target title="Debug-win32-SDL1">
+			<Target title="Debug-win64">
 				<Option platforms="Windows;" />
-				<Option output="bin/Debug/FuzzyLite" prefix_auto="1" extension_auto="1" />
+				<Option output="../FuzzyLite" prefix_auto="1" extension_auto="1" />
 				<Option working_dir="" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x64/" />
 				<Option type="2" />
-				<Option compiler="gcc" />
+				<Option compiler="gnu_gcc_compiler_x64" />
 				<Option createDefFile="1" />
 				<Compiler>
+					<Add option="-Og" />
 					<Add option="-g" />
 				</Compiler>
 			</Target>
-			<Target title="Release-win32-SDL1">
-				<Option platforms="Windows;" />
-				<Option output="bin/Release/FuzzyLite" prefix_auto="1" extension_auto="1" />
-				<Option working_dir="" />
-				<Option object_output="obj/Release/" />
-				<Option type="2" />
-				<Option compiler="gcc" />
-				<Option createDefFile="1" />
-				<Compiler>
-					<Add option="-fomit-frame-pointer" />
-					<Add option="-O2" />
-				</Compiler>
-				<Linker>
-					<Add option="-s" />
-				</Linker>
-			</Target>
 		</Build>
 		<Compiler>
 			<Add option="-Wextra" />
 			<Add option="-Wall" />
+			<Add option="-std=gnu++11" />
 			<Add option="-fexceptions" />
 			<Add option="-Wpointer-arith" />
 			<Add option="-Wno-switch" />
@@ -74,7 +61,6 @@
 			<Add option="-Wno-overloaded-virtual" />
 			<Add option="-DFL_CPP11" />
 			<Add option="-DFL_WINDOWS" />
-			<Add directory="$(#boost.include)" />
 			<Add directory="fuzzylite" />
 		</Compiler>
 		<Unit filename="fuzzylite/fl/Console.h" />

+ 1 - 1
AI/FuzzyLite/FuzzyLite.vcxproj

@@ -251,7 +251,7 @@
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
     <OutDir>..</OutDir>
-    <IncludePath>$(IncludePath)</IncludePath>
+    <IncludePath>$(SolutionDir)\AI\FuzzyLite\fuzzylite;$(IncludePath)</IncludePath>
     <LibraryPath>$(LibraryPath)</LibraryPath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">

+ 3 - 3
AI/StupidAI/CMakeLists.txt

@@ -1,12 +1,12 @@
 project(stupidAI)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 
 set(stupidAI_SRCS
 		StdInc.cpp
-        StupidAI.cpp
-        main.cpp
+		StupidAI.cpp
+		main.cpp
 )
 
 add_library(StupidAI SHARED ${stupidAI_SRCS})

+ 1 - 1
AI/StupidAI/StdInc.cpp

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

+ 6 - 28
AI/StupidAI/StupidAI.cbp

@@ -6,44 +6,20 @@
 		<Option pch_mode="2" />
 		<Option compiler="gcc" />
 		<Build>
-			<Target title="Debug-win32-SDL2">
+			<Target title="Debug-win32">
 				<Option platforms="Windows;" />
 				<Option output="../StupidAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x86" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
 					<Add option="-ggdb" />
 				</Compiler>
 			</Target>
-			<Target title="Release-win32-SDL2">
+			<Target title="Release-win32">
 				<Option platforms="Windows;" />
 				<Option output="../StupidAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
-				<Option type="3" />
-				<Option compiler="gcc" />
-				<Compiler>
-					<Add option="-fomit-frame-pointer" />
-					<Add option="-O3" />
-				</Compiler>
-				<Linker>
-					<Add option="-s" />
-				</Linker>
-			</Target>
-			<Target title="Debug-win32-SDL1">
-				<Option platforms="Windows;" />
-				<Option output="../StupidAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
-				<Option type="3" />
-				<Option compiler="gcc" />
-				<Compiler>
-					<Add option="-ggdb" />
-				</Compiler>
-			</Target>
-			<Target title="Release-win32-SDL1">
-				<Option platforms="Windows;" />
-				<Option output="../StupidAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
+				<Option object_output="obj/Release/x86" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
@@ -59,6 +35,7 @@
 			<Add option="-pedantic" />
 			<Add option="-Wextra" />
 			<Add option="-Wall" />
+			<Add option="-std=gnu++11" />
 			<Add option="-fexceptions" />
 			<Add option="-Wpointer-arith" />
 			<Add option="-Wno-switch" />
@@ -77,6 +54,7 @@
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="StdInc.h">
+			<Option compile="1" />
 			<Option weight="0" />
 		</Unit>
 		<Unit filename="StupidAI.cpp" />

+ 14 - 8
AI/StupidAI/StupidAI.cpp

@@ -5,7 +5,7 @@
 #include "../../CCallback.h"
 #include "../../lib/CCreatureHandler.h"
 
-static shared_ptr<CBattleCallback> cbc;
+static std::shared_ptr<CBattleCallback> cbc;
 
 CStupidAI::CStupidAI(void)
 	: side(-1)
@@ -19,7 +19,7 @@ CStupidAI::~CStupidAI(void)
 	print("destroyed");
 }
 
-void CStupidAI::init(shared_ptr<CBattleCallback> CB)
+void CStupidAI::init(std::shared_ptr<CBattleCallback> CB)
 {
 	print("init called, saving ptr to IBattleCallback");
 	cbc = cb = CB;
@@ -44,7 +44,7 @@ struct EnemyInfo
 	{}
 	void calcDmg(const CStack * ourStack)
 	{
-		TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
+		TDmgRange retal, dmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal);
 		adi = (dmg.first + dmg.second) / 2;
 		adr = (retal.first + retal.second) / 2;
 	}
@@ -108,7 +108,7 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
 	if(stack->type->idNumber == CreatureID::CATAPULT)
 	{
 		BattleAction attack;
-		static const std::vector<int> wallHexes = {50, 183, 182, 130, 62, 29, 12, 95};
+		static const std::vector<int> wallHexes = {50, 183, 182, 130, 78, 29, 12, 95};
 
 		attack.destinationTile = *RandomGeneratorUtil::nextItem(wallHexes, CRandomGenerator::getDefault());
 		attack.actionType = Battle::CATAPULT;
@@ -153,6 +153,12 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
 		}
 	}
 
+	for ( auto & enemy : enemiesReachable )
+		enemy.calcDmg( stack );
+
+	for ( auto & enemy : enemiesShootable )
+		enemy.calcDmg( stack );
+
 	if(enemiesShootable.size())
 	{
 		const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable);
@@ -255,7 +261,7 @@ void CStupidAI::battleStacksRemoved(const BattleStacksRemoved & bsr)
 
 void CStupidAI::print(const std::string &text) const
 {
-    logAi->traceStream() << "CStupidAI [" << this <<"]: " << text;
+	logAi->trace("CStupidAI  [%p]: %s", this, text);
 }
 
 BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
@@ -270,7 +276,7 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
 	auto destNeighbours = destination.neighbouringTiles();
 	if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
 	{
-        logAi->warnStream() << "Warning: already standing on neighbouring tile!";
+		logAi->warn("Warning: already standing on neighbouring tile!");
 		//We shouldn't even be here...
 		return BattleAction::makeDefend(stack);
 	}
@@ -321,13 +327,13 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
 	}
 }
 
-void CStupidAI::saveGame(COSer & h, const int version)
+void CStupidAI::saveGame(BinarySerializer & h, const int version)
 {
 	//TODO to be implemented with saving/loading during the battles
 	assert(0);
 }
 
-void CStupidAI::loadGame(CISer & h, const int version)
+void CStupidAI::loadGame(BinaryDeserializer & h, const int version)
 {
 	//TODO to be implemented with saving/loading during the battles
 	assert(0);

+ 4 - 4
AI/StupidAI/StupidAI.h

@@ -5,14 +5,14 @@
 class CStupidAI : public CBattleGameInterface
 {
 	int side;
-	shared_ptr<CBattleCallback> cb;
+	std::shared_ptr<CBattleCallback> cb;
 
 	void print(const std::string &text) const;
 public:
 	CStupidAI(void);
 	~CStupidAI(void);
 
-	void init(shared_ptr<CBattleCallback> CB) override;
+	void init(std::shared_ptr<CBattleCallback> CB) override;
 	void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
 	void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
 	BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
@@ -36,8 +36,8 @@ public:
 
 	BattleAction goTowards(const CStack * stack, BattleHex hex );
 
-	virtual void saveGame(COSer & h, const int version) override;
-	virtual void loadGame(CISer & h, const int version) override;
+	virtual void saveGame(BinarySerializer & h, const int version) override;
+	virtual void loadGame(BinaryDeserializer & h, const int version) override;
 
 };
 

+ 2 - 2
AI/StupidAI/main.cpp

@@ -25,7 +25,7 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 	strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
 }
 
-extern "C" DLL_EXPORT void GetNewBattleAI(shared_ptr<CBattleGameInterface> &out)
+extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
 {
-	out = make_shared<CStupidAI>();
+	out = std::make_shared<CStupidAI>();
 }

+ 17 - 11
AI/VCAI/AIUtility.cpp

@@ -7,6 +7,10 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/mapObjects/CBank.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
+#include "../../lib/mapObjects/CQuest.h"
+#include "../../lib/CPathfinder.h"
+#include "../../lib/mapping/CMapDefines.h"
 
 /*
  * AIUtility.cpp, part of VCMI engine
@@ -150,7 +154,7 @@ void foreach_neighbour(const int3 &pos, std::function<void(const int3& pos)> foo
 {
 	CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
 
-	for(const int3 &dir : dirs)
+	for(const int3 &dir : int3::getDirs())
 	{
 		const int3 n = pos + dir;
 		if(cbp->isInTheMap(n))
@@ -160,7 +164,7 @@ void foreach_neighbour(const int3 &pos, std::function<void(const int3& pos)> foo
 
 void foreach_neighbour(CCallback * cbp, const int3 &pos, std::function<void(CCallback * cbp, const int3& pos)> foo)
 {
-	for(const int3 &dir : dirs)
+	for(const int3 &dir : int3::getDirs())
 	{
 		const int3 n = pos + dir;
 		if(cbp->isInTheMap(n))
@@ -335,7 +339,7 @@ bool isSafeToVisit(HeroPtr h, crint3 tile)
 	{
 		if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
 		{
-            logAi->traceStream() << boost::format("It's safe for %s to visit tile %s") % h->name % tile;
+			logAi->trace("It's safe for %s to visit tile %s", h->name, tile());
 			return true;
 		}
 		else
@@ -356,10 +360,10 @@ bool canBeEmbarkmentPoint(const TerrainTile *t, bool fromWater)
 int3 whereToExplore(HeroPtr h)
 {
 	TimeCheck tc ("where to explore");
-	int radius = h->getSightRadious();
+	int radius = h->getSightRadius();
 	int3 hpos = h->visitablePos();
 
-	SectorMap sm(h);
+	auto sm = ai->getCachedSectorMap(h);
 
 	//look for nearby objs -> visit them if they're close enouh
 	const int DIST_LIMIT = 3;
@@ -372,9 +376,9 @@ int3 whereToExplore(HeroPtr h)
 			{
 				int3 op = obj->visitablePos();
 				CGPath p;
-				ai->myCb->getPathsInfo(h.get())->getPath(op, p);
+				ai->myCb->getPathsInfo(h.get())->getPath(p, op);
 				if (p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT)
-					if (ai->isGoodForVisit(obj, h, sm))
+					if (ai->isGoodForVisit(obj, h, *sm))
 						nearbyVisitableObjs.push_back(obj);
 			}
 		}
@@ -432,7 +436,7 @@ bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2, CCallback * cbp) //determin
 		for (int y = yMin; y <= yMax; ++y)
 		{
 			int3 tile = int3(x, y, pos1.z); //use only on same level, ofc
-			if (abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5)
+			if (std::abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5)
 			{
 				if (!(cbp->isVisible(tile) && cbp->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good
 					return false;
@@ -505,8 +509,10 @@ bool compareArtifacts(const CArtifactInstance *a1, const CArtifactInstance *a2)
 	auto art1 = a1->artType;
 	auto art2 = a2->artType;
 
-	if (art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL))
+	if(art1->price == art2->price)
+		return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
+	else if(art1->price > art2->price)
 		return true;
 	else
-		return art1->price > art2->price;
-}
+		return false;
+}

+ 8 - 9
AI/VCAI/AIUtility.h

@@ -5,11 +5,9 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/spells/CSpellHandler.h"
-#include "../../lib/Connection.h"
-#include "../../lib/CGameState.h"
-#include "../../lib/mapping/CMap.h"
-#include "../../lib/NetPacks.h"
 #include "../../lib/CStopWatch.h"
+#include "../../lib/mapObjects/CObjectHandler.h"
+#include "../../lib/mapObjects/CGHeroInstance.h"
 
 /*
  * AIUtility.h, part of VCMI engine
@@ -21,10 +19,11 @@
  *
  */
 
+class CCallback;
+
 typedef const int3& crint3;
 typedef const std::string& crstring;
 
-const int HERO_GOLD_COST = 2500;
 const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1;
 const int ACTUAL_RESOURCE_COUNT = 7;
 const int ALLOWED_ROAMING_HEROES = 8;
@@ -44,7 +43,7 @@ struct HeroPtr
 public:
 	std::string name;
 
-	
+
 	HeroPtr();
 	HeroPtr(const CGHeroInstance *H);
 	~HeroPtr();
@@ -107,7 +106,7 @@ struct TimeCheck
 
 	~TimeCheck()
 	{
-        logAi->traceStream() << boost::format("Time of %s was %d ms.") % txt % time.getDiff();
+		logAi->trace("Time of %s was %d ms.",txt,time.getDiff());
 	}
 };
 
@@ -131,10 +130,10 @@ private:
 template<int id>
 bool objWithID(const CGObjectInstance *obj)
 {
-        return obj->ID == id;
+	return obj->ID == id;
 }
 
-std::string strFromInt3(int3 pos);
+std::string strFromInt3(int3 pos);//todo: remove
 void foreach_tile_pos(std::function<void(const int3& pos)> foo);
 void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3& pos)> foo); // avoid costly retrieval of thread-specific pointer
 void foreach_neighbour(const int3 &pos, std::function<void(const int3& pos)> foo);

+ 9 - 8
AI/VCAI/CMakeLists.txt

@@ -2,18 +2,19 @@ project(VCAI)
 cmake_minimum_required(VERSION 2.6)
 
 if (FL_FOUND)
-    include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib ${FL_INCLUDE_DIRS})
+    include_directories(${FL_INCLUDE_DIRS})
 else()
-    include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib ${CMAKE_HOME_DIRECTORY}/AI/FuzzyLite/fuzzylite)
+    include_directories(${CMAKE_HOME_DIRECTORY}/AI/FuzzyLite/fuzzylite)
 endif()
+include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 
 set(VCAI_SRCS
-        StdInc.cpp
-        VCAI.cpp
-        Goals.cpp
-        AIUtility.cpp
-        main.cpp
-        Fuzzy.cpp
+    StdInc.cpp
+    VCAI.cpp
+    Goals.cpp
+    AIUtility.cpp
+    main.cpp
+    Fuzzy.cpp
 )
 
 add_library(VCAI SHARED ${VCAI_SRCS})

+ 18 - 17
AI/VCAI/Fuzzy.cpp

@@ -5,6 +5,8 @@
 #include "../../lib/mapObjects/MapObjects.h"
 #include "../../lib/mapObjects/CommonConstructors.h"
 #include "../../lib/CCreatureHandler.h"
+#include "../../lib/CPathfinder.h"
+#include "../../lib/CGameStateFwd.h"
 #include "../../lib/VCMI_Lib.h"
 #include "../../CCallback.h"
 #include "VCAI.h"
@@ -29,7 +31,6 @@ class Engine;
 class InputVariable;
 class CGTownInstance;
 
-using namespace vstd;
 //using namespace Goals;
 
 FuzzyHelper *fh;
@@ -45,7 +46,7 @@ engineBase::engineBase()
 void engineBase::configure()
 {
 	engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid");
-	logAi->infoStream() << engine.toString();
+	logAi->info(engine.toString());
 }
 
 void engineBase::addRule(const std::string &txt)
@@ -83,7 +84,7 @@ armyStructure evaluateArmyStructure (const CArmedInstance * army)
 		if (walker)
 			walkersStrenght += s.second->getPower();
 
-		amax (maxSpeed, s.second->type->valOfBonuses(Bonus::STACKS_SPEED));
+		vstd::amax(maxSpeed, s.second->type->valOfBonuses(Bonus::STACKS_SPEED));
 	}
 	armyStructure as;
 	as.walkers = walkersStrenght / totalStrenght;
@@ -148,7 +149,7 @@ void FuzzyHelper::initTacticalAdvantage()
 		{
 			fl::Rectangle* none = new fl::Rectangle("NONE", CGTownInstance::NONE, CGTownInstance::NONE + (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f);
 			ta.castleWalls->addTerm(none);
-                    
+
 			fl::Trapezoid* medium = new fl::Trapezoid("MEDIUM", (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f, CGTownInstance::FORT,
 				CGTownInstance::CITADEL, CGTownInstance::CITADEL + (CGTownInstance::CASTLE - CGTownInstance::CITADEL) * 0.5f);
 			ta.castleWalls->addTerm(medium);
@@ -159,7 +160,7 @@ void FuzzyHelper::initTacticalAdvantage()
 			ta.castleWalls->setRange(CGTownInstance::NONE, CGTownInstance::CASTLE);
 		}
 
-		
+
 
 		ta.bankPresent = new fl::InputVariable("Bank");
 		ta.engine.addInputVariable(ta.bankPresent);
@@ -200,7 +201,7 @@ void FuzzyHelper::initTacticalAdvantage()
 	}
 	catch (fl::Exception & pe)
 	{
-		logAi->errorStream() << "initTacticalAdvantage " << ": " << pe.getWhat();
+		logAi->error("initTacticalAdvantage: %s", pe.getWhat());
 	}
 }
 
@@ -261,7 +262,7 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
 	}
 	catch (fl::Exception & fe)
 	{
-		logAi->errorStream() << "getTacticalAdvantage " << ": " << fe.getWhat();
+		logAi->error("getTacticalAdvantage: %s ",fe.getWhat());
 	}
 
 	if (output < 0 || (output != output))
@@ -272,7 +273,7 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
 
 		for (int i = 0; i < boost::size(tab); i++)
 			log << names[i] << ": " << tab[i]->getInputValue() << " ";
-		logAi->errorStream() << log.str();
+		logAi->error(log.str());
 		assert(false);
 	}
 
@@ -295,13 +296,15 @@ FuzzyHelper::TacticalAdvantage::~TacticalAdvantage()
 	delete threat;
 }
 
-//shared_ptr<AbstractGoal> chooseSolution (std::vector<shared_ptr<AbstractGoal>> & vec)
+//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec)
 
 Goals::TSubgoal FuzzyHelper::chooseSolution (Goals::TGoalVec vec)
 {
 	if (vec.empty()) //no possibilities found
 		return sptr(Goals::Invalid());
 
+	ai->cachedSectorMaps.clear();
+
 	//a trick to switch between heroes less often - calculatePaths is costly
 	auto sortByHeroes = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
 	{
@@ -407,7 +410,7 @@ void FuzzyHelper::initVisitTile()
 	}
 	catch (fl::Exception & fe)
 	{
-		logAi->errorStream() << "visitTile " << ": " << fe.getWhat();
+		logAi->error("visitTile: %s",fe.getWhat());
 	}
 }
 
@@ -419,7 +422,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
 
 	//assert(cb->isInTheMap(g.tile));
 	float turns = 0;
-	float distance = cb->getMovementCost(g.hero.h, g.tile);
+	float distance = CPathfinderHelper::getMovementCost(g.hero.h, g.tile);
 	if (!distance) //we stand on that tile
 		turns = 0;
 	else
@@ -452,11 +455,10 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
 	}
 	catch (fl::Exception & fe)
 	{
-		logAi->errorStream() << "evaluate VisitTile " << ": " << fe.getWhat();
+		logAi->error("evaluate VisitTile: %s",fe.getWhat());
 	}
 	assert (g.priority >= 0);
 	return g.priority;
-
 }
 float FuzzyHelper::evaluate (Goals::VisitHero & g)
 {
@@ -465,7 +467,7 @@ float FuzzyHelper::evaluate (Goals::VisitHero & g)
 		return -100; //hero died in the meantime
 	//TODO: consider direct copy (constructor?)
 	g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).setisAbstract(g.isAbstract).accept(this));
-	return g.priority;	
+	return g.priority;
 }
 float FuzzyHelper::evaluate (Goals::GatherArmy & g)
 {
@@ -480,8 +482,7 @@ float FuzzyHelper::evaluate (Goals::ClearWayTo & g)
 	if (!g.hero.h)
 		throw cannotFulfillGoalException("ClearWayTo called without hero!");
 
-	SectorMap sm(g.hero);
-	int3 t = sm.firstTileToGet(g.hero, g.tile);
+	int3 t = ai->getCachedSectorMap(g.hero)->firstTileToGet(g.hero, g.tile);
 
 	if (t.valid())
 	{
@@ -523,7 +524,7 @@ float FuzzyHelper::evaluate (Goals::Invalid & g)
 }
 float FuzzyHelper::evaluate (Goals::AbstractGoal & g)
 {
-	logAi->warnStream() << boost::format("Cannot evaluate goal %s") % g.name();
+	logAi->warn("Cannot evaluate goal %s", g.name());
 	return g.priority;
 }
 void FuzzyHelper::setPriority (Goals::TSubgoal & g)

+ 4 - 1
AI/VCAI/Fuzzy.h

@@ -15,6 +15,7 @@
 class VCAI;
 class CArmedInstance;
 class CBank;
+struct SectorMap;
 
 class engineBase
 {
@@ -54,6 +55,8 @@ class FuzzyHelper
 		~EvalVisitTile();
 	} vt;
 
+	
+
 public:
 	enum RuleBlocks {BANK_DANGER, TACTICAL_ADVANTAGE, VISIT_TILE};
 	//blocks should be initialized in this order, which may be confusing :/
@@ -80,5 +83,5 @@ public:
 	float getTacticalAdvantage (const CArmedInstance *we, const CArmedInstance *enemy); //returns factor how many times enemy is stronger than us
 
 	Goals::TSubgoal chooseSolution (Goals::TGoalVec vec);
-	//shared_ptr<AbstractGoal> chooseSolution (std::vector<shared_ptr<AbstractGoal>> & vec);
+	//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
 };

+ 72 - 42
AI/VCAI/Goals.cpp

@@ -3,6 +3,7 @@
 #include "VCAI.h"
 #include "Fuzzy.h"
 #include "../../lib/mapping/CMap.h" //for victory conditions
+#include "../../lib/CPathfinder.h"
 
 /*
  * Goals.cpp, part of VCMI engine
@@ -18,12 +19,11 @@ extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
 extern FuzzyHelper * fh; //TODO: this logic should be moved inside VCAI
 
-using namespace vstd;
 using namespace Goals;
 
 TSubgoal Goals::sptr(const AbstractGoal & tmp)
 {
-	shared_ptr<AbstractGoal> ptr;
+	std::shared_ptr<AbstractGoal> ptr;
 	ptr.reset(tmp.clone());
 	return ptr;
 }
@@ -154,8 +154,8 @@ bool Goals::AbstractGoal::operator== (AbstractGoal &g)
 
 //TODO: find out why the following are not generated automatically on MVS?
 
-namespace Goals 
-{ 
+namespace Goals
+{
 	template <>
 	void CGoal<Win>::accept (VCAI * ai)
 	{
@@ -182,8 +182,8 @@ namespace Goals
 
 //TSubgoal AbstractGoal::whatToDoToAchieve()
 //{
-//    logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
-//        return sptr (Goals::Explore());
+//	logAi->debug("Decomposing goal of type %s",name());
+//	return sptr (Goals::Explore());
 //}
 
 TSubgoal Win::whatToDoToAchieve()
@@ -275,7 +275,7 @@ TSubgoal Win::whatToDoToAchieve()
 					// 0.85 -> radius now 2 tiles
 					// 0.95 -> 1 tile radius, position is fully known
 					// AFAIK H3 AI does something like this
-					int3 grailPos = cb->getGrailPos(ratio);
+					int3 grailPos = cb->getGrailPos(&ratio);
 					if(ratio > 0.99)
 					{
 						return sptr (Goals::DigAtTile(grailPos));
@@ -367,7 +367,7 @@ TSubgoal FindObj::whatToDoToAchieve()
 
 std::string GetObj::completeMessage() const
 {
-	return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid); 
+	return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
 }
 
 TSubgoal GetObj::whatToDoToAchieve()
@@ -409,7 +409,7 @@ bool GetObj::fulfillsMe (TSubgoal goal)
 
 std::string VisitHero::completeMessage() const
 {
-	return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid); 
+	return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
 }
 
 TSubgoal VisitHero::whatToDoToAchieve()
@@ -422,7 +422,7 @@ TSubgoal VisitHero::whatToDoToAchieve()
 	if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
 	{
 		if (hero->pos == pos)
-			logAi->errorStream() << "Hero " << hero.name << " tries to visit himself.";
+			logAi->error("Hero %s tries to visit himself.", hero.name);
 		else
 		{
 			//can't use VISIT_TILE here as tile appears blocked by target hero
@@ -435,10 +435,17 @@ TSubgoal VisitHero::whatToDoToAchieve()
 
 bool VisitHero::fulfillsMe (TSubgoal goal)
 {
-	if (goal->goalType == Goals::VISIT_TILE && cb->getObj(ObjectInstanceID(objid))->visitablePos() == goal->tile)
-		return true;
-	else
+	if (goal->goalType != Goals::VISIT_TILE)
+	{
+		return false;
+	}
+	auto obj = cb->getObj(ObjectInstanceID(objid));
+	if (!obj)
+	{
+		logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile, objid);
 		return false;
+	}
+	return obj->visitablePos() == goal->tile;
 }
 
 TSubgoal GetArtOfType::whatToDoToAchieve()
@@ -454,11 +461,11 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 	assert(cb->isInTheMap(tile)); //set tile
 	if(!cb->isVisible(tile))
 	{
-        logAi->errorStream() << "Clear way should be used with visible tiles!";
+		logAi->error("Clear way should be used with visible tiles!");
 		return sptr (Goals::Explore());
 	}
 
-	return (fh->chooseSolution(getAllPossibleSubgoals()));	
+	return (fh->chooseSolution(getAllPossibleSubgoals()));
 }
 
 TGoalVec ClearWayTo::getAllPossibleSubgoals()
@@ -483,9 +490,9 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 
 		//if our hero is trapped, make sure we request clearing the way from OUR perspective
 
-		SectorMap sm(h);
+		auto sm = ai->getCachedSectorMap(h);
 
-		int3 tileToHit = sm.firstTileToGet(h, tile);
+		int3 tileToHit = sm->firstTileToGet(h, tile);
 		if (!tileToHit.valid())
 			continue;
 
@@ -505,7 +512,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 
 			if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
 				if (topObj != hero.get(true)) //the hero we want to free
-					logAi->errorStream() << boost::format("%s stands in the way of %s") % topObj->getObjectName()  % h->getObjectName();
+					logAi->error("%s stands in the way of %s", topObj->getObjectName(), h->getObjectName());
 			if (topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
 			{
 				if (shouldVisit(h, topObj))
@@ -517,11 +524,11 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 				else
 				{
 					//TODO: we should be able to return apriopriate quest here (VCAI::striveToQuest)
-					logAi->debugStream() << "Quest guard blocks the way to " + tile();
+					logAi->debug("Quest guard blocks the way to %s", tile());
+					continue; //do not access quets guard if we can't complete the quest
 				}
 			}
 		}
-
 		if (isSafeToVisit(h, tileToHit)) //this makes sense only if tile is guarded, but there i no quest object
 		{
 			ret.push_back (sptr (Goals::VisitTile(tileToHit).sethero(h)));
@@ -537,7 +544,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 
 	if (ret.empty())
 	{
-		logAi->warnStream() << "There is no known way to clear the way to tile " + tile();
+		logAi->warn("There is no known way to clear the way to tile %s",tile());
 		throw goalFulfilledException (sptr(Goals::ClearWayTo(tile))); //make sure asigned hero gets unlocked
 	}
 
@@ -574,7 +581,7 @@ TGoalVec Explore::getAllPossibleSubgoals()
 	{
 		//heroes = ai->getUnblockedHeroes();
 		heroes = cb->getHeroesInfo();
-		erase_if (heroes, [](const HeroPtr h)
+		vstd::erase_if(heroes, [](const HeroPtr h)
 		{
 			if (ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer
 				return true;
@@ -633,11 +640,11 @@ TGoalVec Explore::getAllPossibleSubgoals()
 
 	for (auto h : heroes)
 	{
-		SectorMap sm(h);
+		auto sm = ai->getCachedSectorMap(h);
 
 		for (auto obj : objs) //double loop, performance risk?
 		{
-			auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
+			auto t = sm->firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
 			if (ai->isTileNotReserved(h, t))
 				ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
 		}
@@ -692,8 +699,8 @@ TSubgoal RecruitHero::whatToDoToAchieve()
 	if(!t)
 		return sptr (Goals::BuildThis(BuildingID::TAVERN));
 
-	if(cb->getResourceAmount(Res::GOLD) < HERO_GOLD_COST)
-		return sptr (Goals::CollectRes(Res::GOLD, HERO_GOLD_COST));
+	if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
+		return sptr (Goals::CollectRes(Res::GOLD, GameConstants::HERO_GOLD_COST));
 
 	return iAmElementar();
 }
@@ -746,10 +753,10 @@ TGoalVec VisitTile::getAllPossibleSubgoals()
 		if (ai->canRecruitAnyHero())
 			ret.push_back (sptr(Goals::RecruitHero()));
 	}
-	if (ret.empty())
+	if(ret.empty())
 	{
-		auto obj = frontOrNull(cb->getVisitableObjs(tile));
-		if (obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
+		auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
+		if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
 		{
 			if (hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
 				ret.push_back(sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
@@ -766,7 +773,7 @@ TGoalVec VisitTile::getAllPossibleSubgoals()
 
 TSubgoal DigAtTile::whatToDoToAchieve()
 {
-	const CGObjectInstance *firstObj = frontOrNull(cb->getVisitableObjs(tile));
+	const CGObjectInstance *firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
 	if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
 	{
 		const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(firstObj);
@@ -862,7 +869,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
 		{
 			auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
 			if(!creatures)
-				continue; 
+				continue;
 
 			int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
 			if(upgradeNumber < 0)
@@ -957,13 +964,13 @@ TGoalVec Conquer::getAllPossibleSubgoals()
 	std::vector<const CGObjectInstance *> objs;
 	for (auto obj : ai->visitableObjs)
 	{
-		if (conquerable(obj)) 
+		if (conquerable(obj))
 			objs.push_back (obj);
 	}
 
 	for (auto h : cb->getHeroesInfo())
 	{
-		SectorMap sm(h);
+		auto sm = ai->getCachedSectorMap(h);
 		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
 
 		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
@@ -971,11 +978,30 @@ TGoalVec Conquer::getAllPossibleSubgoals()
 			if (conquerable(obj))
 				ourObjs.push_back(obj);
 		}
-		for (auto obj : ourObjs) //double loop, performance risk?
+		for (auto obj : ourObjs)
 		{
-			auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
-			if (ai->isTileNotReserved(h, t))
-				ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
+			int3 dest = obj->visitablePos();
+			auto t = sm->firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded
+			if (t.valid()) //we know any path at all
+			{
+				if (ai->isTileNotReserved(h, t)) //no other hero wants to conquer that tile
+				{
+					if (isSafeToVisit(h, dest))
+					{
+						if (dest != t) //there is something blocking our way
+							ret.push_back(sptr(Goals::ClearWayTo(dest, h).setisAbstract(true)));
+						else
+						{
+							if (obj->ID.num == Obj::HERO) //enemy hero may move to other position
+								ret.push_back(sptr(Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true)));
+							else //just visit that tile
+								ret.push_back(sptr(Goals::VisitTile(dest).sethero(h).setisAbstract(true)));
+						}
+					}
+					else //we need to get army in order to conquer that place
+						ret.push_back(sptr(Goals::GatherArmy(evaluateDanger(dest, h) * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true)));
+				}
+			}
 		}
 	}
 	if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
@@ -1008,11 +1034,15 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 
 	return fh->chooseSolution (getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
 }
+
+static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
+	BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
+
 TGoalVec GatherArmy::getAllPossibleSubgoals()
 {
 	//get all possible towns, heroes and dwellings we may use
 	TGoalVec ret;
-	
+
 	//TODO: include evaluation of monsters gather in calculation
 	for (auto t : cb->getTownsInfo())
 	{
@@ -1033,7 +1063,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 
 	auto otherHeroes = cb->getHeroesInfo();
 	auto heroDummy = hero;
-	erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
+	vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
 	{
 		return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->visitablePos(), h, true)
 			|| !ai->canGetArmy(heroDummy.h, h) || ai->getGoal(h)->goalType == Goals::GATHER_ARMY);
@@ -1074,11 +1104,11 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 	}
 	for(auto h : cb->getHeroesInfo())
 	{
-		SectorMap sm(h);
+		auto sm = ai->getCachedSectorMap(h);
 		for (auto obj : objs)
 		{ //find safe dwelling
 			auto pos = obj->visitablePos();
-			if (ai->isGoodForVisit(obj, h, sm))
+			if (ai->isGoodForVisit(obj, h, *sm))
 				ret.push_back (sptr (Goals::VisitTile(pos).sethero(h)));
 		}
 	}
@@ -1098,7 +1128,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 
 //TSubgoal AbstractGoal::whatToDoToAchieve()
 //{
-//    logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
+//	logAi->debug("Decomposing goal of type %s",name());
 //	return sptr (Goals::Explore());
 //}
 

+ 10 - 7
AI/VCAI/Goals.h

@@ -21,7 +21,7 @@ class FuzzyHelper;
 
 namespace Goals
 {
-	struct AbstractGoal;
+	class AbstractGoal;
 	class VisitTile;
 	typedef std::shared_ptr<Goals::AbstractGoal> TSubgoal;
 	typedef std::vector<TSubgoal> TGoalVec;
@@ -86,8 +86,10 @@ public:
 		value = 0;
 		aid = -1;
 		resID = -1;
+		objid = -1;
 		tile = int3(-1, -1, -1);
 		town = nullptr;
+		bid = -1;
 	}
 	virtual ~AbstractGoal(){};
 	//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
@@ -107,7 +109,7 @@ public:
 	static TSubgoal tryRecruitHero();
 
 	///Visitor pattern
-	//TODO: make accept work for shared_ptr... somehow
+	//TODO: make accept work for std::shared_ptr... somehow
 	virtual void accept (VCAI * ai); //unhandled goal will report standard error
 	virtual float accept (FuzzyHelper * f);
 
@@ -134,6 +136,7 @@ public:
 		isAbstract = false;
 		value = 0;
 		aid = -1;
+		objid = -1;
 		resID = -1;
 		tile = int3(-1, -1, -1);
 		town = nullptr;
@@ -161,7 +164,7 @@ public:
 	TSubgoal iAmElementar()
 	{
 		setisElementar(true);
-		shared_ptr<AbstractGoal> ptr;
+		std::shared_ptr<AbstractGoal> ptr;
 		ptr.reset(clone());
 		return ptr;
 	}
@@ -300,7 +303,7 @@ public:
 	VisitHero(int hid) : CGoal (Goals::VISIT_HERO){objid = hid; priority = 4;};
 	TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
 	TSubgoal whatToDoToAchieve() override;
-	//bool operator== (VisitHero &g) {return g.objid == objid;}
+	bool operator== (VisitHero &g) { return g.goalType == goalType && g.objid == objid; }
 	bool fulfillsMe (TSubgoal goal) override;
 	std::string completeMessage() const override;
 };
@@ -322,7 +325,7 @@ public:
 	VisitTile(int3 Tile) : CGoal (Goals::VISIT_TILE) {tile = Tile; priority = 5;};
 	TGoalVec getAllPossibleSubgoals() override;
 	TSubgoal whatToDoToAchieve() override;
-	//bool operator== (VisitTile &g) {return g.tile == tile;}
+	bool operator== (VisitTile &g) { return g.goalType == goalType && g.tile == tile; }
 	std::string completeMessage() const override;
 }; 
 class ClearWayTo : public CGoal<ClearWayTo>
@@ -334,7 +337,7 @@ public:
 	ClearWayTo(int3 Tile, HeroPtr h) : CGoal (Goals::CLEAR_WAY_TO) {tile = Tile; hero = h; priority = 5;};
 	TGoalVec getAllPossibleSubgoals() override;
 	TSubgoal whatToDoToAchieve() override;
-	bool operator== (ClearWayTo &g) {return g.tile == tile;}
+	bool operator== (ClearWayTo &g) { return g.goalType == goalType && g.tile == tile; }
 };
 class DigAtTile : public CGoal<DigAtTile>
 	//elementar with hero on tile
@@ -345,7 +348,7 @@ public:
 	DigAtTile(int3 Tile) : CGoal (Goals::DIG_AT_TILE) {tile = Tile; priority = 20;};
 	TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
 	TSubgoal whatToDoToAchieve() override;
-	bool operator== (DigAtTile &g) {return g.tile == tile;}
+	bool operator== (DigAtTile &g) { return g.goalType == goalType && g.tile == tile; }
 };
 
 class CIssueCommand : public CGoal<CIssueCommand>

+ 17 - 26
AI/VCAI/VCAI.cbp

@@ -6,23 +6,25 @@
 		<Option pch_mode="2" />
 		<Option compiler="gcc" />
 		<Build>
-			<Target title="Debug-win32-SDL2">
+			<Target title="Debug-win32">
 				<Option platforms="Windows;" />
 				<Option output="../VCAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x86" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
+					<Add option="-Og" />
 					<Add option="-g" />
 				</Compiler>
 				<Linker>
-					<Add directory="../FuzzyLite/bin/Debug" />
+					<Add directory="../" />
+					<Add directory="$(#boost.lib32)" />
 				</Linker>
 			</Target>
-			<Target title="Release-win32-SDL2">
+			<Target title="Release-win32">
 				<Option platforms="Windows;" />
 				<Option output="../VCAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
+				<Option object_output="obj/Release/x86" />
 				<Option type="3" />
 				<Option compiler="gcc" />
 				<Compiler>
@@ -31,35 +33,23 @@
 				</Compiler>
 				<Linker>
 					<Add option="-s" />
-					<Add directory="../FuzzyLite/bin/Release" />
+					<Add directory="../" />
+					<Add directory="$(#boost.lib32)" />
 				</Linker>
 			</Target>
-			<Target title="Debug-win32-SDL1">
+			<Target title="Debug-win64">
 				<Option platforms="Windows;" />
 				<Option output="../VCAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Debug/" />
+				<Option object_output="obj/Debug/x64" />
 				<Option type="3" />
-				<Option compiler="gcc" />
+				<Option compiler="gnu_gcc_compiler_x64" />
 				<Compiler>
+					<Add option="-Og" />
 					<Add option="-g" />
 				</Compiler>
 				<Linker>
-					<Add directory="../FuzzyLite/bin/Debug" />
-				</Linker>
-			</Target>
-			<Target title="Release-win32-SDL1">
-				<Option platforms="Windows;" />
-				<Option output="../VCAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
-				<Option object_output="obj/Release/" />
-				<Option type="3" />
-				<Option compiler="gcc" />
-				<Compiler>
-					<Add option="-fomit-frame-pointer" />
-					<Add option="-O3" />
-				</Compiler>
-				<Linker>
-					<Add option="-s" />
-					<Add directory="../FuzzyLite/bin/Release" />
+					<Add directory="../" />
+					<Add directory="$(#boost.lib64)" />
 				</Linker>
 			</Target>
 		</Build>
@@ -67,6 +57,7 @@
 			<Add option="-pedantic" />
 			<Add option="-Wextra" />
 			<Add option="-Wall" />
+			<Add option="-std=gnu++11" />
 			<Add option="-fexceptions" />
 			<Add option="-Wpointer-arith" />
 			<Add option="-Wno-switch" />
@@ -86,7 +77,6 @@
 			<Add option="-lboost_thread$(#boost.libsuffix)" />
 			<Add option="-lboost_chrono$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib32)" />
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="AIUtility.cpp" />
@@ -96,6 +86,7 @@
 		<Unit filename="Goals.cpp" />
 		<Unit filename="Goals.h" />
 		<Unit filename="StdInc.h">
+			<Option compile="1" />
 			<Option weight="0" />
 		</Unit>
 		<Unit filename="VCAI.cpp" />

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 269 - 163
AI/VCAI/VCAI.cpp


+ 21 - 29
AI/VCAI/VCAI.h

@@ -12,11 +12,8 @@
 #include "../../lib/CBuildingHandler.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"
+#include "../../lib/mapObjects/MiscObjects.h"
 #include "../../lib/spells/CSpellHandler.h"
-#include "../../lib/Connection.h"
-#include "../../lib/CGameState.h"
-#include "../../lib/mapping/CMap.h"
-#include "../../lib/NetPacks.h"
 #include "../../lib/CondSh.h"
 
 struct QuestInfo;
@@ -81,7 +78,7 @@ struct SectorMap
 		int id;
 		std::vector<int3> tiles;
 		std::vector<int3> embarkmentPoints; //tiles of other sectors onto which we can (dis)embark
-		std::vector<const CGObjectInstance *> subterraneanGates;
+		std::vector<const CGObjectInstance *> visitableObjs;
 		bool water; //all tiles of sector are land or water
 		Sector()
 		{
@@ -95,6 +92,7 @@ struct SectorMap
 	//std::vector<std::vector<std::vector<unsigned char>>> pathfinderSector;
 
 	std::map<int, Sector> infoOnSectors;
+	std::shared_ptr<boost::multi_array<TerrainTile*, 3>> visibleTiles;
 
 	SectorMap();
 	SectorMap(HeroPtr h);
@@ -103,7 +101,11 @@ struct SectorMap
 	void exploreNewSector(crint3 pos, int num, CCallback * cbp);
 	void write(crstring fname);
 
+	bool markIfBlocked(ui8 &sec, crint3 pos, const TerrainTile *t);
+	bool markIfBlocked(ui8 &sec, crint3 pos);
 	unsigned char &retreiveTile(crint3 pos);
+	TerrainTile* getTile(crint3 pos) const;
+	std::vector<const CGObjectInstance *> getNearbyObjs(HeroPtr h, bool sectorsAround);
 
 	void makeParentBFS(crint3 source);
 
@@ -111,20 +113,6 @@ struct SectorMap
 	int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
 };
 
-//Set of buildings for different goals. Does not include any prerequisites.
-const BuildingID essential[] = {BuildingID::TAVERN, BuildingID::TOWN_HALL};
-const BuildingID goldSource[] = {BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL};
-const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
-	BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
-const BuildingID unitsUpgrade[] = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
-	BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP};
-const BuildingID unitGrowth[] = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
-	BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR};
-const BuildingID spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
-	BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5};
-const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
-	BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings
-
 class VCAI : public CAdventureAI
 {
 public:
@@ -141,9 +129,10 @@ public:
 
 	friend class FuzzyHelper;
 
-	std::map<TeleportChannelID, shared_ptr<TeleportChannel> > knownTeleportChannels;
+	std::map<TeleportChannelID, std::shared_ptr<TeleportChannel> > knownTeleportChannels;
 	std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
 	ObjectInstanceID destinationTeleport;
+	int3 destinationTeleportPos;
 	std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
 	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
 	std::map<HeroPtr, std::set<const CGTownInstance *> > townVisitsThisWeek;
@@ -157,17 +146,19 @@ public:
 	std::set<const CGObjectInstance *> alreadyVisited;
 	std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
 
+	std::map <HeroPtr, std::shared_ptr<SectorMap>> cachedSectorMaps; //TODO: serialize? not necessary
+
 	TResources saving;
 
 	AIStatus status;
 	std::string battlename;
 
-	shared_ptr<CCallback> myCb;
+	std::shared_ptr<CCallback> myCb;
 
-	unique_ptr<boost::thread> makingTurn;
+	std::unique_ptr<boost::thread> makingTurn;
 
 	VCAI(void);
-	~VCAI(void);
+	virtual ~VCAI(void);
 
 	//TODO: use only smart pointers?
 	void tryRealize(Goals::Explore & g);
@@ -189,16 +180,16 @@ public:
 
 	virtual std::string getBattleAIName() const override;
 
-	virtual void init(shared_ptr<CCallback> CB) override;
+	virtual void init(std::shared_ptr<CCallback> CB) override;
 	virtual void yourTurn() override;
 
 	virtual void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
 	virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
 	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
-	virtual void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
-	virtual void saveGame(COSer & h, const int version) override; //saving
-	virtual void loadGame(CISer & h, const int version) override; //loading
+	virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
+	virtual void saveGame(BinarySerializer & h, const int version) override; //saving
+	virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading
 	virtual void finish() override;
 
 	virtual void availableCreaturesChanged(const CGDwelling *town) override;
@@ -268,7 +259,6 @@ public:
 
 	void recruitHero(const CGTownInstance * t, bool throwing = false);
 	bool isGoodForVisit(const CGObjectInstance *obj, HeroPtr h, SectorMap &sm);
-	std::vector<const CGObjectInstance *> getPossibleDestinations(HeroPtr h);
 	void buildStructure(const CGTownInstance * t);
 	//void recruitCreatures(const CGTownInstance * t);
 	void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
@@ -292,7 +282,7 @@ public:
 	void markHeroUnableToExplore (HeroPtr h);
 	void markHeroAbleToExplore (HeroPtr h);
 	bool isAbleToExplore (HeroPtr h);
-	void clearHeroesUnableToExplore();
+	void clearPathsInfo();
 
 	void validateObject(const CGObjectInstance *obj); //checks if object is still visible and if not, removes references to it
 	void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
@@ -307,6 +297,8 @@ public:
 
 	const CGObjectInstance *getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> &predicate);
 	bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
+	//optimization - use one SM for every hero call
+	std::shared_ptr<SectorMap> getCachedSectorMap(HeroPtr h);
 
 	const CGTownInstance *findTownWithTavern() const;
 	bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;

+ 2 - 2
AI/VCAI/main.cpp

@@ -23,7 +23,7 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 	strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
 }
 
-extern "C" DLL_EXPORT void GetNewAI(shared_ptr<CGlobalAI> &out)
+extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out)
 {
-	out = make_shared<VCAI>();
+	out = std::make_shared<VCAI>();
 }

+ 5 - 2
AUTHORS

@@ -5,7 +5,7 @@ Michał Urbańczyk aka Tow,        <[email protected]>
 maintenance, reverse engineering, general support.
 
 Mateusz B. aka Tow dragon,                <[email protected]>
-   * general suport, battle support, support for many Heroes 3 config files, reverse engineering, ERM/VERM parser and interpreter
+   * general support, battle support, support for many Heroes 3 config files, reverse engineering, ERM/VERM parser and interpreter
 
 Stefan Pavlov aka Ste,            <[email protected]>
    * minor fixes in pregame
@@ -52,5 +52,8 @@ Alexey aka Macron1Robot,                 <>
 Alexander Shishkin aka alexvins,
    * MinGW platform support, modding related programming
 
-Arseniy Shestakov aka SXX,
+Arseniy Shestakov aka SXX,      <[email protected]>
    * pathfinding improvements, programming
+
+Vadim Markovtsev, <[email protected]>
+   * resolving problems with macOS, bug fixes

+ 13 - 22
CCallback.cpp

@@ -12,18 +12,12 @@
 #include "lib/mapObjects/CObjectClassesHandler.h"
 #include "lib/CGeneralTextHandler.h"
 #include "lib/CHeroHandler.h"
-#include "lib/Connection.h"
 #include "lib/NetPacks.h"
 #include "client/mapHandler.h"
 #include "lib/spells/CSpellHandler.h"
 #include "lib/CArtHandler.h"
 #include "lib/GameConstants.h"
-#ifdef min
-#undef min
-#endif
-#ifdef max
-#undef max
-#endif
+#include "lib/CPlayerState.h"
 #include "lib/UnlockGuard.h"
 
 /*
@@ -60,7 +54,7 @@ int CCallback::selectionMade(int selection, QueryID queryID)
 	ASSERT_IF_CALLED_WITH_PLAYER
 	if(queryID == QueryID(-1))
 	{
-        logGlobal->errorStream() << "Cannot answer the query -1!";
+		logGlobal->errorStream() << "Cannot answer the query -1!";
 		return false;
 	}
 
@@ -71,7 +65,8 @@ int CCallback::selectionMade(int selection, QueryID queryID)
 
 void CCallback::recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level/*=-1*/)
 {
-	if(player!=obj->tempOwner  &&  obj->ID != Obj::WAR_MACHINE_FACTORY)
+	// TODO exception for neutral dwellings shouldn't be hardcoded
+	if(player != obj->tempOwner && obj->ID != Obj::WAR_MACHINE_FACTORY && obj->ID != Obj::REFUGEE_CAMP)
 		return;
 
 	RecruitCreatures pack(obj->id, dst->id, ID, amount, level);
@@ -80,7 +75,7 @@ void CCallback::recruitCreatures(const CGDwelling *obj, const CArmedInstance * d
 
 bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
 {
-	if(((player>=0)  &&  obj->tempOwner != player) || (obj->stacksCount()<2  && obj->needsLastStack()))
+	if((player && obj->tempOwner != player) || (obj->stacksCount()<2  && obj->needsLastStack()))
 		return false;
 
 	DisbandCreature pack(stackPos,obj->id);
@@ -97,7 +92,7 @@ bool CCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, Crea
 
 void CCallback::endTurn()
 {
-    logGlobal->traceStream() << "Player " << *player << " ended his turn.";
+	logGlobal->traceStream() << "Player " << *player << " ended his turn.";
 	EndTurn pack;
 	sendRequest(&pack); //report that we ended turn
 }
@@ -189,7 +184,7 @@ int CBattleCallback::sendRequest(const CPack *request)
 	int requestID = cl->sendRequest(request, *player);
 	if(waitTillRealize)
 	{
-        logGlobal->traceStream() << boost::format("We'll wait till request %d is answered.\n") % requestID;
+		logGlobal->traceStream() << boost::format("We'll wait till request %d is answered.\n") % requestID;
 		auto gsUnlocker = vstd::makeUnlockSharedGuardIf(getGsMutex(), unlockGsWhenWaiting);
 		cl->waitingRequest.waitWhileContains(requestID);
 	}
@@ -230,7 +225,6 @@ void CCallback::trade(const CGObjectInstance *market, EMarketMode::EMarketMode m
 
 void CCallback::setFormation(const CGHeroInstance * hero, bool tight)
 {
-	const_cast<CGHeroInstance*>(hero)-> formation = tight;
 	SetFormation pack(hero->id,tight);
 	sendRequest(&pack);
 }
@@ -290,11 +284,6 @@ bool CCallback::canMoveBetween(const int3 &a, const int3 &b)
 	return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a);
 }
 
-int CCallback::getMovementCost(const CGHeroInstance * hero, int3 dest)
-{
-	return gs->getMovementCost(hero, hero->visitablePos(), dest, hero->hasBonusOfType (Bonus::FLYING_MOVEMENT), hero->movement);
-}
-
 const CPathsInfo * CCallback::getPathsInfo(const CGHeroInstance *h)
 {
 	return cl->getPathsInfo(h);
@@ -331,6 +320,8 @@ void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int
 
 void CCallback::unregisterAllInterfaces()
 {
+	for (auto& pi : cl->playerint)
+		pi.second->finish();
 	cl->playerint.clear();
 	cl->battleints.clear();
 }
@@ -343,22 +334,22 @@ int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance
 		return swapCreatures(s1, s2, p1, p2);
 }
 
-void CCallback::registerGameInterface(shared_ptr<IGameEventsReceiver> gameEvents)
+void CCallback::registerGameInterface(std::shared_ptr<IGameEventsReceiver> gameEvents)
 {
 	cl->additionalPlayerInts[*player].push_back(gameEvents);
 }
 
-void CCallback::registerBattleInterface(shared_ptr<IBattleEventsReceiver> battleEvents)
+void CCallback::registerBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents)
 {
 	cl->additionalBattleInts[*player].push_back(battleEvents);
 }
 
-void CCallback::unregisterGameInterface(shared_ptr<IGameEventsReceiver> gameEvents)
+void CCallback::unregisterGameInterface(std::shared_ptr<IGameEventsReceiver> gameEvents)
 {
 	cl->additionalPlayerInts[*player] -= gameEvents;
 }
 
-void CCallback::unregisterBattleInterface(shared_ptr<IBattleEventsReceiver> battleEvents)
+void CCallback::unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents)
 {
 	cl->additionalBattleInts[*player] -= battleEvents;
 }

+ 25 - 26
CCallback.h

@@ -104,49 +104,48 @@ public:
 
 	//client-specific functionalities (pathfinding)
 	virtual bool canMoveBetween(const int3 &a, const int3 &b);
-	virtual int getMovementCost(const CGHeroInstance * hero, int3 dest);
 	virtual int3 getGuardingCreaturePosition(int3 tile);
 	virtual const CPathsInfo * getPathsInfo(const CGHeroInstance *h);
 
 	virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out);
 
 	//Set of metrhods that allows adding more interfaces for this player that'll receive game event call-ins.
-	void registerGameInterface(shared_ptr<IGameEventsReceiver> gameEvents);
-	void registerBattleInterface(shared_ptr<IBattleEventsReceiver> battleEvents);
-	void unregisterGameInterface(shared_ptr<IGameEventsReceiver> gameEvents);
-	void unregisterBattleInterface(shared_ptr<IBattleEventsReceiver> battleEvents);
+	void registerGameInterface(std::shared_ptr<IGameEventsReceiver> gameEvents);
+	void registerBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
+	void unregisterGameInterface(std::shared_ptr<IGameEventsReceiver> gameEvents);
+	void unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
 
 	void unregisterAllInterfaces(); //stops delivering information about game events to player interfaces -> can be called ONLY after victory/loss
 
 //commands
-	bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false); //dst must be free, neighbouring tile (this function can move hero only by one tile)
+	bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false) override; //dst must be free, neighbouring tile (this function can move hero only by one tile)
 	bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where);
-	int selectionMade(int selection, QueryID queryID);
-	int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2);
-	int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2); //first goes to the second
-	int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2); //first goes to the second
-	int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val);
-	bool dismissHero(const CGHeroInstance * hero);
+	int selectionMade(int selection, QueryID queryID) override;
+	int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override;
+	int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second
+	int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second
+	int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val) override;
+	bool dismissHero(const CGHeroInstance * hero) override;
 	//bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2);
-	bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2);
+	bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2) override;
 	//bool moveArtifact(const CGHeroInstance * hero, ui16 src, const CStackInstance * stack, ui16 dest); // TODO: unify classes
 	//bool moveArtifact(const CStackInstance * stack, ui16 src , const CGHeroInstance * hero, ui16 dest); // TODO: unify classes
-	bool assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo);
+	bool assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
 	bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
-	void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1);
-	bool dismissCreature(const CArmedInstance *obj, SlotID stackPos);
+	void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override;
+	bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;
 	bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override;
-	void endTurn();
-	void swapGarrisonHero(const CGTownInstance *town);
+	void endTurn() override;
+	void swapGarrisonHero(const CGTownInstance *town) override;
 	void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
-	void trade(const CGObjectInstance *market, EMarketMode::EMarketMode mode, int id1, int id2, int val1, const CGHeroInstance *hero = nullptr);
-	void setFormation(const CGHeroInstance * hero, bool tight);
-	void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero);
-	void save(const std::string &fname);
-	void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr);
-	void buildBoat(const IShipyard *obj);
-	void dig(const CGObjectInstance *hero);
-	void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1));
+	void trade(const CGObjectInstance *market, EMarketMode::EMarketMode mode, int id1, int id2, int val1, const CGHeroInstance *hero = nullptr) override;
+	void setFormation(const CGHeroInstance * hero, bool tight) override;
+	void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) override;
+	void save(const std::string &fname) override;
+	void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) override;
+	void buildBoat(const IShipyard *obj) override;
+	void dig(const CGObjectInstance *hero) override;
+	void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1)) override;
 
 //friends
 	friend class CClient;

+ 29 - 27
CMakeLists.txt

@@ -13,15 +13,20 @@ endif()
 
 # VCMI version
 set(VCMI_VERSION_MAJOR 0)
-set(VCMI_VERSION_MINOR 98)
+set(VCMI_VERSION_MINOR 99)
 set(VCMI_VERSION_PATCH 0)
 
 option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF)
 option(ENABLE_EDITOR "Enable compilation of map editor" OFF)
 option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
-option(ENABLE_TEST "Enable compilation of unit tests" OFF)
+option(ENABLE_TEST "Enable compilation of unit tests" ON)
 option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
-option(ENABLE_SDL2 "Use SDL2 for compilation instead of SDL 1.2" ON)
+
+############################################
+#        Documentation section             #
+############################################
+
+include(UseDoxygen OPTIONAL)
 
 ############################################
 #        Building section                  #
@@ -59,7 +64,7 @@ endif()
 if (WIN32)
 	add_definitions(-DBOOST_THREAD_USE_LIB)
 	add_definitions(-D_WIN32_WINNT=0x0501)
-	set(SYSTEM_LIBS ${SYSTEM_LIBS} ole32 oleaut32 ws2_32 mswsock)
+	set(SYSTEM_LIBS ${SYSTEM_LIBS} ole32 oleaut32 ws2_32 mswsock dbghelp)
 
 	#delete lib prefix for dlls (libvcmi -> vcmi)
 	set(CMAKE_SHARED_LIBRARY_PREFIX "") 
@@ -110,7 +115,7 @@ endif()
 set(SYSTEM_LIBS ${SYSTEM_LIBS} ${CMAKE_DL_LIBS})
 
 set(FFmpeg_FIND_COMPONENTS AVFORMAT SWSCALE)
-find_package(Boost 1.48.0 COMPONENTS filesystem locale program_options system thread REQUIRED)
+find_package(Boost 1.48.0 COMPONENTS date_time filesystem locale program_options system thread REQUIRED)
 find_package(ZLIB REQUIRED)
 find_package(FFmpeg REQUIRED)
 find_package(Minizip)
@@ -118,26 +123,20 @@ if (MINIZIP_FOUND)
     add_definitions(-DUSE_SYSTEM_MINIZIP)
 endif()
 
-if (ENABLE_SDL2)
-	find_package(SDL2 REQUIRED)
-	find_package(SDL2_image REQUIRED)
-	find_package(SDL2_mixer REQUIRED)
-	find_package(SDL2_ttf REQUIRED)
-
-	set(SDL_INCLUDE_DIR "${SDL2_INCLUDE_DIR}")
-	set(SDLTTF_INCLUDE_DIR "${SDL2_TTF_INCLUDE_DIR}")
-	set(SDLIMAGE_INCLUDE_DIR "${SDL2_IMAGE_INCLUDE_DIR}")
-	set(SDLMIXER_INCLUDE_DIR "${SDL2_MIXER_INCLUDE_DIR}")
-	set(SDL_LIBRARY "${SDL2_LIBRARY}")
-	set(SDLTTF_LIBRARY "${SDL2_TTF_LIBRARY}")
-	set(SDLIMAGE_LIBRARY "${SDL2_IMAGE_LIBRARY}")
-	set(SDLMIXER_LIBRARY "${SDL2_MIXER_LIBRARY}")
-else()
-	find_package(SDL REQUIRED)
-	find_package(SDL_image REQUIRED)
-	find_package(SDL_mixer REQUIRED)
-	find_package(SDL_ttf REQUIRED)
-endif()
+find_package(SDL2 REQUIRED)
+find_package(SDL2_image REQUIRED)
+find_package(SDL2_mixer REQUIRED)
+find_package(SDL2_ttf REQUIRED)
+
+set(SDL_INCLUDE_DIR "${SDL2_INCLUDE_DIR}")
+set(SDLTTF_INCLUDE_DIR "${SDL2_TTF_INCLUDE_DIR}")
+set(SDLIMAGE_INCLUDE_DIR "${SDL2_IMAGE_INCLUDE_DIR}")
+set(SDLMIXER_INCLUDE_DIR "${SDL2_MIXER_INCLUDE_DIR}")
+set(SDL_LIBRARY "${SDL2_LIBRARY}")
+set(SDLTTF_LIBRARY "${SDL2_TTF_LIBRARY}")
+set(SDLIMAGE_LIBRARY "${SDL2_IMAGE_LIBRARY}")
+set(SDLMIXER_LIBRARY "${SDL2_MIXER_LIBRARY}")
+
 include(cotire)
 
 if (ENABLE_EDITOR OR ENABLE_LAUNCHER)
@@ -151,7 +150,7 @@ endif()
 
 if(ENABLE_TEST)
 	# find_package overwrites BOOST_* variables which are already set, so all components have to be included again
-	find_package(Boost 1.48.0 COMPONENTS program_options filesystem system thread locale unit_test_framework REQUIRED)
+	find_package(Boost 1.48.0 COMPONENTS date_time program_options filesystem system thread locale unit_test_framework REQUIRED)
 endif()
 
 if(CMAKE_COMPILER_IS_GNUCXX OR NOT WIN32) #so far all *nix compilers support such parameters
@@ -236,7 +235,7 @@ endif()
 #    Installation section             #
 #######################################
 
-# For apple this files will be already inside vcmiclient bundle
+# For apple these files will be already inside vcmiclient bundle
 if (NOT APPLE)
 	# copy whole directory but .svn control files
 	install(DIRECTORY config DESTINATION ${DATA_DIR} PATTERN ".svn" EXCLUDE)
@@ -343,3 +342,6 @@ endif()
 
 INCLUDE(CPack)
 
+include(GetGitRevisionDescription)
+get_git_head_revision(GIT_REFSPEC GIT_SHA1)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Version.cpp.in" "${CMAKE_BINARY_DIR}/Version.cpp" @ONLY)

+ 27 - 2
ChangeLog

@@ -1,14 +1,39 @@
-0.98 -> 0.next
+0.98 -> 0.99
+
+GENERAL:
+* New Bonus NO_TERRAIN_PENALTY
+* Nomads will remove Sand movement penalty from army
+* Flying and water walking is now supported in pathfinder
+* New artifacts supported
+- Angel Wings
+- Boots of Levitation
+* Implemented rumors in tavern window
+* New cheat codes:
+- vcmiglaurung - gives 5000 crystal dragons into each slot
+- vcmiungoliant - conceal fog of war for current player
+* New console commands:
+- gosolo - AI take control over human players and vice versa
+- controlai - give control of one or all AIs to player
+- set hideSystemMessages on/off - supress server messages in chat
+
+BATTLES:
+* Drawbridge mechanics implemented (animation still missing)
+* Merging of town and visiting hero armies on siege implemented
+* Hero info tooltip for skills and mana implemented
 
 ADVETURE AI:
 * Fixed AI trying to go through underground rock
 * Fixed several cases causing AI wandering aimlessly
-* AI can again pick bets artifacts and exchange artifacts between heroes
+* AI can again pick best artifacts and exchange artifacts between heroes
+* AI heroes with patrol enabled won't leave patrol area anymore
 
 RANDOM MAP GENERATOR:
 * Changed fractalization algorithm so it can create cycles
 * Zones will not have straight paths anymore, they are totally random
+* Generated zones will have different size depending on template setting
 * Added Thieves Guild random object (1 per zone)
+* Added Seer Huts with quests that match OH3
+* RMG will guarantee at least 100 pairs of Monoliths are available even if there are not enough different defs
 
 0.97 -> 0.98
 GENERAL:

+ 38 - 18
Global.h

@@ -55,13 +55,13 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #  define VCMI_UNIX
 #  define VCMI_XDG
 #  ifdef __ANDROID__
-#    define VCMI_ANDROID 
+#    define VCMI_ANDROID
 #  endif
 #elif defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
 #  define VCMI_UNIX
 #  define VCMI_XDG
 #  define VCMI_FREEBSD
-#elif defined(__GNU__) || defined(__gnu_hurd__) || (defined(__MACH__) && !defined(__APPLE))
+#elif defined(__GNU__) || defined(__gnu_hurd__) || (defined(__MACH__) && !defined(__APPLE__))
 #  define VCMI_UNIX
 #  define VCMI_XDG
 #  define VCMI_HURD
@@ -93,8 +93,25 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #ifdef VCMI_WINDOWS
 #  define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers - delete this line if something is missing.
 #  define NOMINMAX					// Exclude min/max macros from <Windows.h>. Use std::[min/max] from <algorithm> instead.
+#  define _NO_W32_PSEUDO_MODIFIERS  // Exclude more macros for compiling with MinGW on Linux.
 #endif
 
+/* ---------------------------------------------------------------------------- */
+/* A macro to force inlining some of our functions */
+/* ---------------------------------------------------------------------------- */
+// Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower
+#ifdef _MSC_VER
+#  define STRONG_INLINE __forceinline
+#elif __GNUC__
+#  define STRONG_INLINE inline __attribute__((always_inline))
+#else
+#  define STRONG_INLINE inline
+#endif
+
+#define TO_STRING_HELPER(x) #x
+#define TO_STRING(x) TO_STRING_HELPER(x)
+#define LINE_IN_FILE __FILE__ ":" TO_STRING(__LINE__)
+
 #define _USE_MATH_DEFINES
 
 #include <cstdio>
@@ -133,6 +150,10 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #define BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE 1
 #define BOOST_BIND_NO_PLACEHOLDERS
 
+#if defined(_MSC_VER) && (_MSC_VER == 1900)
+#define BOOST_NO_CXX11_VARIADIC_TEMPLATES //Variadic templates are buggy in VS2015, so turn this off to avoid compile errors
+#endif
+
 #include <boost/algorithm/string.hpp>
 #include <boost/cstdint.hpp>
 #include <boost/current_function.hpp>
@@ -150,6 +171,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #endif
 #include <boost/logic/tribool.hpp>
 #include <boost/optional.hpp>
+#include <boost/optional/optional_io.hpp>
 #include <boost/program_options.hpp>
 #include <boost/range/adaptor/filtered.hpp>
 #include <boost/range/adaptor/reversed.hpp>
@@ -157,6 +179,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <boost/thread.hpp>
 #include <boost/variant.hpp>
 #include <boost/math/special_functions/round.hpp>
+#include <boost/multi_array.hpp>
 
 #ifndef M_PI
 #  define M_PI 3.14159265358979323846
@@ -165,9 +188,6 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 /* ---------------------------------------------------------------------------- */
 /* Usings */
 /* ---------------------------------------------------------------------------- */
-using std::shared_ptr;
-using std::unique_ptr;
-using std::make_shared;
 using namespace std::placeholders;
 namespace range = boost::range;
 
@@ -206,7 +226,6 @@ typedef boost::lock_guard<boost::recursive_mutex> TLockGuardRec;
 #    define DLL_IMPORT	__attribute__ ((visibility("default")))
 #    define DLL_EXPORT __attribute__ ((visibility("default")))
 #    define ELF_VISIBILITY __attribute__ ((visibility("default")))
-#    define ELF_VISIBILITY __attribute__ ((visibility("default")))
 #  endif
 #endif
 
@@ -230,7 +249,8 @@ template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];
 /* ---------------------------------------------------------------------------- */
 /* VCMI standard library */
 /* ---------------------------------------------------------------------------- */
-#include "lib/logging/CLogger.h"
+#include <vstd/CLoggerBase.h>
+#include "lib/logging/CLogger.h" //todo: remove
 
 void inline handleException()
 {
@@ -240,15 +260,15 @@ void inline handleException()
 	}
 	catch(const std::exception & ex)
 	{
-		logGlobal->errorStream() << ex.what();
+		logGlobal->error(ex.what());
 	}
 	catch(const std::string & ex)
 	{
-		logGlobal->errorStream() << ex;
+		logGlobal->error(ex);
 	}
 	catch(...)
 	{
-		logGlobal->errorStream() << "Sorry, caught unknown exception type. No more info available.";
+		logGlobal->error("Sorry, caught unknown exception type. No more info available.");
 	}
 }
 
@@ -273,7 +293,7 @@ std::ostream & operator<<(std::ostream & out, const std::vector<T> & container)
 
 namespace vstd
 {
-	
+
 	// combine hashes. Present in boost but not in std
 	template <class T>
 	inline void hash_combine(std::size_t& seed, const T& v)
@@ -281,7 +301,7 @@ namespace vstd
 		std::hash<T> hasher;
 		seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
 	}
-	
+
 	//returns true if container c contains item i
 	template <typename Container, typename Item>
 	bool contains(const Container & c, const Item &i)
@@ -493,7 +513,7 @@ namespace vstd
 	void erase_if(std::set<Elem> &setContainer, Predicate pred)
 	{
 		auto itr = setContainer.begin();
-		auto endItr = setContainer.end(); 
+		auto endItr = setContainer.end();
 		while(itr != endItr)
 		{
 			auto tmpItr = itr++;
@@ -507,7 +527,7 @@ namespace vstd
 	void erase_if(std::map<Key, Val> &container, Predicate pred)
 	{
 		auto itr = container.begin();
-		auto endItr = container.end(); 
+		auto endItr = container.end();
 		while(itr != endItr)
 		{
 			auto tmpItr = itr++;
@@ -542,7 +562,7 @@ namespace vstd
 			return vf(lhs) < vf(rhs);
 		});
 	}
-		
+
 	//Returns iterator to the element for which the value of ValueFunction is maximal
 	template<class ForwardRange, class ValueFunction>
 	auto maxElementByFun(const ForwardRange& rng, ValueFunction vf) -> decltype(std::begin(rng))
@@ -615,7 +635,7 @@ namespace vstd
 	{
 		if(index < r.size())
 			return r[index];
-		
+
 		return defaultValue;
 	}
 
@@ -656,12 +676,12 @@ namespace vstd
 		boost::sort(vec);
 		vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
 	}
-	
+
 	template <typename T>
 	void concatenate(std::vector<T> &dest, const std::vector<T> &src)
 	{
 		dest.reserve(dest.size() + src.size());
-		dest.insert(dest.end(), src.begin(), src.end());	
+		dest.insert(dest.end(), src.begin(), src.end());
 	}
 
 	template <typename T>

+ 7 - 0
Mods/vcmi/Sprites/ScSelC.json

@@ -0,0 +1,7 @@
+{
+        "basepath" : "mapFormatIcons/",
+	"images" :
+	[
+		{ "group" : 1, "frame" : 0, "file" : "vcmi1.png"}
+	]
+}

BIN
Mods/vcmi/Sprites/mapFormatIcons/vcmi1.png


+ 19 - 2
README.linux

@@ -20,7 +20,7 @@ To compile, the following packages (and their development counterparts) are need
 	* zlib and zlib-devel
 	* (optional) Qt 5, widget and network modules
 	* the ffmpeg libraries (libavformat and libswscale). Their name could be libavformat-devel and libswscale-devel, or ffmpeg-libs-devel or similar names.
-	* boost c++ libraries v1.48+ (www.boost.org):
+	* boost c++ libraries v1.50+ (www.boost.org):
 		- program-options
 		- filesystem
 		- system
@@ -28,11 +28,17 @@ To compile, the following packages (and their development counterparts) are need
 		- locale
 
 On Debian-based systems (e.g. Ubuntu) run:
-  sudo apt-get install cmake g++ libsdl1.2debian libsdl-image1.2-dev libsdl-ttf2.0-dev libsdl-mixer1.2-dev zlib1g-dev libavformat-dev libswscale-dev libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev qtbase5-dev
+  sudo apt-get install cmake g++ libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev zlib1g-dev libavformat-dev libswscale-dev libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev libboost-test-dev qtbase5-dev
 
 On RPM-based distributions (e.g. Fedora) run:
   sudo yum install cmake gcc-c++ SDL2-devel SDL2_image-devel SDL2_ttf-devel SDL2_mixer-devel boost boost-devel boost-filesystem boost-system boost-thread boost-program-options boost-locale zlib-devel ffmpeg-devel ffmpeg-libs qt5-qtbase-devel 
 
+On Arch-based distributions, there is a development package available for VCMI on the AUR. It can be found at:
+  https://aur.archlinux.org/packages/vcmi-git/
+
+  Information about building packages from the Arch User Repository (AUR) can be
+  found at the Arch wiki.
+
 II. Getting the sources
 
 VCMI is still in development. We recommend the following initial directory structure:
@@ -93,3 +99,14 @@ Go to /LIB_PATH/vcmi/AI, and type:
 Go to /DATA_PATH/vcmi, and type:
   ln -s .../trunk/source/config
   ln -s .../trunk/source/Mods
+
+IV. Compiling documentation
+
+To compile using Doxygen, the UseDoxygen CMake module must be installed. It can
+be fetched from: http://tobias.rautenkranz.ch/cmake/doxygen/
+
+Once UseDoxygen is installed, run:
+    cmake .
+    make doc
+
+The built documentation will be available from ./doc

+ 2 - 1
README.md

@@ -1,3 +1,4 @@
+[![Build Status](https://travis-ci.org/vcmi/vcmi.svg?branch=develop)](https://travis-ci.org/vcmi/vcmi)
 # VCMI Project
 VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities. To use VCMI you need to own original data files.
 
@@ -18,4 +19,4 @@ For building from source see project wiki at http://wiki.vcmi.eu/
 VCMI Project source code is licensed under GPL version 2 or later.
 VCMI Project assets are licensed under CC-BY-SA 4.0. Assets sources and information about contributors are available under following link: [https://github.com/vcmi/vcmi-assets]
 
-Copyright (C) 2007-2015  VCMI Team (check AUTHORS file for the contributors list)
+Copyright (C) 2007-2016  VCMI Team (check AUTHORS file for the contributors list)

+ 1 - 1
VCMI_global.props

@@ -7,7 +7,7 @@
   <PropertyGroup>
     <_PropertySheetDisplayName>VCMI_global</_PropertySheetDisplayName>
     <LibraryPath>$(SolutionDir)..\libs\$(PlatformShortName);$(VCMI_Out);$(LibraryPath)</LibraryPath>
-    <IncludePath>$(SolutionDir)..\include;$(IncludePath)</IncludePath>
+    <IncludePath>$(SolutionDir)..\include;$(SolutionDir)include;$(IncludePath)</IncludePath>
     <OutDir>$(VCMI_Out)\</OutDir>
   </PropertyGroup>
   <ItemDefinitionGroup>

+ 6 - 0
Version.cpp.in

@@ -0,0 +1,6 @@
+#include "Version.h"
+
+namespace GameConstants
+{
+const char GIT_SHA1[] = "@GIT_SHA1@";
+}

+ 4 - 0
Version.h

@@ -0,0 +1,4 @@
+namespace GameConstants
+{
+extern const char GIT_SHA1[];
+}

+ 16 - 8
client/CBitmapHandler.cpp

@@ -1,11 +1,9 @@
 #include "StdInc.h"
 
 #include "../lib/filesystem/Filesystem.h"
-#include "../lib/filesystem/CFileInfo.h"
 #include "SDL.h"
 #include "SDL_image.h"
 #include "CBitmapHandler.h"
-#include "CDefHandler.h"
 #include "gui/SDL_Extensions.h"
 #include "../lib/vcmi_endian.h"
 
@@ -19,6 +17,14 @@
  *
  */
 
+
+namespace BitmapHandler
+{
+	SDL_Surface * loadH3PCX(ui8 * data, size_t size);
+
+	SDL_Surface * loadBitmapFromDir(std::string path, std::string fname, bool setKey=true);
+}
+
 bool isPCX(const ui8 *header)//check whether file can be PCX according to header
 {
 	int fSize  = read_le_u32(header + 0);
@@ -70,7 +76,7 @@ SDL_Surface * BitmapHandler::loadH3PCX(ui8 * pcx, size_t size)
 			tp.r = pcx[it++];
 			tp.g = pcx[it++];
 			tp.b = pcx[it++];
-			CSDL_Ext::colorSetAlpha(tp,SDL_ALPHA_OPAQUE);
+			tp.a = SDL_ALPHA_OPAQUE;
 			ret->format->palette->colors[i] = tp;
 		}
 	}
@@ -102,7 +108,7 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna
 {
 	if(!fname.size())
 	{
-        logGlobal->warnStream() << "Call to loadBitmap with void fname!";
+		logGlobal->warnStream() << "Call to loadBitmap with void fname!";
 		return nullptr;
 	}
 	if (!CResourceHandler::get()->existsResource(ResourceID(path + fname, EResType::IMAGE)))
@@ -142,13 +148,13 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna
 			{
 				//set correct value for alpha\unused channel
 				for (int i=0; i < ret->format->palette->ncolors; i++)
-					CSDL_Ext::colorSetAlpha(ret->format->palette->colors[i],SDL_ALPHA_OPAQUE);				
+					ret->format->palette->colors[i].a = SDL_ALPHA_OPAQUE;
 			}
 		}
 		else
 		{
-            logGlobal->errorStream()<<"Failed to open "<<fname<<" via SDL_Image";
-			logGlobal->errorStream()<<"Reason: " << IMG_GetError();
+			logGlobal->errorStream() << "Failed to open " << fname << " via SDL_Image";
+			logGlobal->errorStream() << "Reason: " << IMG_GetError();
 			return nullptr;
 		}
 	}
@@ -174,7 +180,9 @@ SDL_Surface * BitmapHandler::loadBitmap(std::string fname, bool setKey)
 
 	if (!(bitmap = loadBitmapFromDir("DATA/", fname, setKey)) &&
 		!(bitmap = loadBitmapFromDir("SPRITES/", fname, setKey)))
-        logGlobal->errorStream()<<"Error: Failed to find file "<<fname;
+	{
+		logGlobal->errorStream() << "Error: Failed to find file " << fname;
+	}
 
 	return bitmap;
 }

+ 1 - 4
client/CBitmapHandler.h

@@ -14,9 +14,6 @@ struct SDL_Surface;
 
 namespace BitmapHandler
 {
-	SDL_Surface * loadH3PCX(ui8 * data, size_t size);
-	//Load file from specific LOD
-	SDL_Surface * loadBitmapFromDir(std::string path, std::string fname, bool setKey=true);
-	//Load file from any LODs
+	//Load file from /DATA or /SPRITES
 	SDL_Surface * loadBitmap(std::string fname, bool setKey=true);
 }

+ 5 - 29
client/CDefHandler.cpp

@@ -67,7 +67,7 @@ void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
 		palette[it].r = de.palette[it].R;
 		palette[it].g = de.palette[it].G;
 		palette[it].b = de.palette[it].B;
-		CSDL_Ext::colorSetAlpha(palette[it],SDL_ALPHA_OPAQUE);	
+		palette[it].a = SDL_ALPHA_OPAQUE;	
 	}
 
 	// The SDefEntryBlock starts just after the SDefEntry
@@ -122,12 +122,6 @@ void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
 	}
 }
 
-void CDefHandler::expand(ui8 N,ui8 & BL, ui8 & BR)
-{
-	BL = (N & 0xE0) >> 5;
-	BR = N & 0x1F;
-}
-
 SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const
 {
 	SDL_Surface * ret=nullptr;
@@ -180,24 +174,11 @@ SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Co
 
 	BaseOffset += sizeof(SSpriteDef);
 	int BaseOffsetor = BaseOffset;
-
-	#ifdef VCMI_SDL1
-	for(int i=0; i<256; ++i)
-	{		
-		SDL_Color pr;
-		pr.r = palette[i].r;
-		pr.g = palette[i].g;
-		pr.b = palette[i].b;
-		pr.unused = palette[i].unused;
-		(*(ret->format->palette->colors+i))=pr;		
-	}
-	#else
-	if(SDL_SetPaletteColors(ret->format->palette,palette,0,256) != 0)
-	{
-		throw std::runtime_error("Unable to set palette");	
-	}
 	
-	#endif
+	SDL_Palette * p = SDL_AllocPalette(256);	
+	SDL_SetPaletteColors(p, palette, 0, 256);
+	SDL_SetSurfacePalette(ret, p);
+	SDL_FreePalette(p);	
 
 	int ftcp=0;
 
@@ -363,13 +344,8 @@ SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Co
 	}
 
 	SDL_Color ttcol = ret->format->palette->colors[0];
-	#ifdef VCMI_SDL1
-	Uint32 keycol = SDL_MapRGBA(ret->format, ttcol.r, ttcol.b, ttcol.g, ttcol.unused);	
-	SDL_SetColorKey(ret, SDL_SRCCOLORKEY, keycol);	
-	#else
 	Uint32 keycol = SDL_MapRGBA(ret->format, ttcol.r, ttcol.b, ttcol.g, ttcol.a);	
 	SDL_SetColorKey(ret, SDL_TRUE, keycol);	
-	#endif // 0
 
 	return ret;
 }

+ 4 - 4
client/CDefHandler.h

@@ -85,7 +85,9 @@ private:
 		int group;
 	} ;
 	std::vector<SEntry> SEntries ;
-
+	
+	void openFromMemory(ui8 * table, const std::string & name);	
+	SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const;
 public:
 	int width, height; //width and height
 	std::string defName;
@@ -94,9 +96,7 @@ public:
 
 	CDefHandler(); //c-tor
 	~CDefHandler(); //d-tor
-	SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const; //saves picture with given number to "testtt.bmp"
-	static void expand(ui8 N,ui8 & BL, ui8 & BR);
-	void openFromMemory(ui8 * table, const std::string & name);
+	
 	CDefEssential * essentialize();
 
 	static CDefHandler * giveDef(const std::string & defName);

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 293 - 253
client/CMT.cpp


+ 1 - 4
client/CMT.h

@@ -1,6 +1,5 @@
 #pragma once
 
-#ifndef VCMI_SDL1
 #include <SDL_render.h>
 
 extern SDL_Texture * screenTexture;
@@ -8,8 +7,6 @@ extern SDL_Texture * screenTexture;
 extern SDL_Window * mainWindow;
 extern SDL_Renderer * mainRenderer;
 
-#endif // VCMI_SDL2
-
 extern SDL_Surface *screen;      // main screen surface
 extern SDL_Surface *screen2;     // and hlp surface (used to store not-active interfaces layer)
 extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
@@ -17,4 +14,4 @@ extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present
 
 extern bool gNoGUI; //if true there is no client window and game is silently played between AIs
 
-void handleQuit();
+void handleQuit(bool ask = true);

+ 6 - 4
client/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(vcmiclient)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 include_directories(${SDL_INCLUDE_DIR} ${SDLIMAGE_INCLUDE_DIR} ${SDLMIXER_INCLUDE_DIR} ${SDLTTF_INCLUDE_DIR})
 include_directories(${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS})
 
@@ -57,6 +57,7 @@ set(client_SRCS
 		Graphics.cpp
 		mapHandler.cpp
 		NetPacksClient.cpp
+		SDLRWwrapper.cpp
 )
 
 set(client_HEADERS
@@ -102,7 +103,7 @@ if(APPLE)
 		cp -r ${CMAKE_HOME_DIRECTORY}/osx/vcmibuilder.app ${BUNDLE_PATH}/MacOS/vcmibuilder.app &&
 
 		# Copy frameworks
-		cp -r ${CMAKE_HOME_DIRECTORY}/${CMAKE_FRAMEWORK_PATH} ${BUNDLE_PATH}/Frameworks/ &&
+		sh -c 'cp -r ${CMAKE_HOME_DIRECTORY}/${CMAKE_FRAMEWORK_PATH} ${BUNDLE_PATH}/Frameworks/ || true' &&
 
 		# Copy vcmi data
 		mkdir -p ${BUNDLE_PATH}/Data &&
@@ -110,9 +111,10 @@ if(APPLE)
 		mkdir -p ${BUNDLE_PATH}/Data/launcher &&
 		cp -r ${CMAKE_HOME_DIRECTORY}/config/ ${BUNDLE_PATH}/Data/config/ &&
 		cp -r ${CMAKE_HOME_DIRECTORY}/Mods/vcmi/ ${BUNDLE_PATH}/Data/Mods/vcmi/ &&
-		cp -r ${CMAKE_HOME_DIRECTORY}/Mods/WoG/ ${BUNDLE_PATH}/Data/Mods/WoG/ &&
+		sh -c 'cp -r ${CMAKE_HOME_DIRECTORY}/Mods/WoG/ ${BUNDLE_PATH}/Data/Mods/WoG/ || echo "Download WoG mod from http://wiki.vcmi.eu/index.php?title=Mod_list" ' &&
+		sh -c 'cp -r ${CMAKE_HOME_DIRECTORY}/Mods/hota/ ${BUNDLE_PATH}/Data/Mods/hota/ || echo "Download HOTA mod from http://wiki.vcmi.eu/index.php?title=Mod_list" ' &&
 		cp -r ${CMAKE_HOME_DIRECTORY}/launcher/icons/ ${BUNDLE_PATH}/Data/launcher/icons/)
-		
+
 	add_custom_command(TARGET vcmiclient POST_BUILD COMMAND ${MakeVCMIBundle})
 elseif(WIN32)
 	add_executable(vcmiclient ${client_SRCS} VCMI_client.rc)

+ 1 - 2
client/CMessage.cpp

@@ -15,7 +15,6 @@
 #include "CGameInfo.h"
 #include "gui/SDL_Extensions.h"
 #include "../lib/CGeneralTextHandler.h"
-#include "Graphics.h"
 #include "windows/GUIClasses.h"
 #include "../lib/CConfigHandler.h"
 #include "CBitmapHandler.h"
@@ -45,7 +44,7 @@ public:
 	CComponent *comp;
 
 	//blit component with image centered at this position
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 
 	//ComponentResolved(); //c-tor
 	ComponentResolved(CComponent *Comp); //c-tor

+ 23 - 41
client/CMusicHandler.cpp

@@ -3,6 +3,7 @@
 
 #include "CMusicHandler.h"
 #include "CGameInfo.h"
+#include "SDLRWwrapper.h"
 #include "../lib/CCreatureHandler.h"
 #include "../lib/spells/CSpellHandler.h"
 #include "../lib/JsonNode.h"
@@ -51,7 +52,7 @@ void CAudioBase::init()
 
 	if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1)
 	{
-        logGlobal->errorStream() << "Mix_OpenAudio error: " << Mix_GetError();
+		logGlobal->errorStream() << "Mix_OpenAudio error: " << Mix_GetError();
 		return;
 	}
 
@@ -86,7 +87,7 @@ CSoundHandler::CSoundHandler():
 	listener(std::bind(&CSoundHandler::onVolumeChange, this, _1));
 
 	// Vectors for helper(s)
-	pickupSounds = 
+	pickupSounds =
 	{
 		soundBase::pickup01, soundBase::pickup02, soundBase::pickup03,
 		soundBase::pickup04, soundBase::pickup05, soundBase::pickup06, soundBase::pickup07
@@ -127,8 +128,8 @@ void CSoundHandler::release()
 
 		for (auto &chunk : soundChunks)
 		{
-			if (chunk.second)
-				Mix_FreeChunk(chunk.second);
+			if (chunk.second.first)
+				Mix_FreeChunk(chunk.second.first);
 		}
 	}
 
@@ -141,20 +142,20 @@ Mix_Chunk *CSoundHandler::GetSoundChunk(std::string &sound, bool cache)
 	try
 	{
 		if (cache && soundChunks.find(sound) != soundChunks.end())
-			return soundChunks[sound];
+			return soundChunks[sound].first;
 
 		auto data = CResourceHandler::get()->load(ResourceID(std::string("SOUNDS/") + sound, EResType::SOUND))->readAll();
-		SDL_RWops *ops = SDL_RWFromMem(data.first.release(), data.second);
+		SDL_RWops *ops = SDL_RWFromMem(data.first.get(), data.second);
 		Mix_Chunk *chunk = Mix_LoadWAV_RW(ops, 1);	// will free ops
 
 		if (cache)
-			soundChunks.insert(std::pair<std::string, Mix_Chunk *>(sound, chunk));
+			soundChunks.insert(std::pair<std::string, CachedChunk>(sound, std::make_pair (chunk, std::move (data.first))));
 
 		return chunk;
 	}
 	catch(std::exception &e)
 	{
-        logGlobal->warnStream() << "Cannot get sound " << sound << " chunk: " << e.what();
+		logGlobal->warnStream() << "Cannot get sound " << sound << " chunk: " << e.what();
 		return nullptr;
 	}
 }
@@ -182,7 +183,7 @@ int CSoundHandler::playSound(std::string sound, int repeats, bool cache)
 		channel = Mix_PlayChannel(-1, chunk, repeats);
 		if (channel == -1)
 		{
-            logGlobal->errorStream() << "Unable to play sound file " << sound << " , error " << Mix_GetError();
+			logGlobal->errorStream() << "Unable to play sound file " << sound << " , error " << Mix_GetError();
 			if (!cache)
 				Mix_FreeChunk(chunk);
 		}
@@ -303,7 +304,7 @@ void CMusicHandler::release()
 
 void CMusicHandler::playMusic(std::string musicURI, bool loop)
 {
-	if (current && current->isTrack( musicURI))
+	if (current && current->isTrack(musicURI))
 		return;
 
 	queueNext(this, "", musicURI, loop);
@@ -314,7 +315,7 @@ void CMusicHandler::playMusicFromSet(std::string whichSet, bool loop)
 	auto selectedSet = musicsSet.find(whichSet);
 	if (selectedSet == musicsSet.end())
 	{
-        logGlobal->errorStream() << "Error: playing music from non-existing set: " << whichSet;
+		logGlobal->errorStream() << "Error: playing music from non-existing set: " << whichSet;
 		return;
 	}
 
@@ -331,25 +332,25 @@ void CMusicHandler::playMusicFromSet(std::string whichSet, int entryID, bool loo
 	auto selectedSet = musicsSet.find(whichSet);
 	if (selectedSet == musicsSet.end())
 	{
-        logGlobal->errorStream() << "Error: playing music from non-existing set: " << whichSet;
+		logGlobal->errorStream() << "Error: playing music from non-existing set: " << whichSet;
 		return;
 	}
 
 	auto selectedEntry = selectedSet->second.find(entryID);
 	if (selectedEntry == selectedSet->second.end())
 	{
-        logGlobal->errorStream() << "Error: playing non-existing entry " << entryID << " from set: " << whichSet;
+		logGlobal->errorStream() << "Error: playing non-existing entry " << entryID << " from set: " << whichSet;
 		return;
 	}
 
-	if (current && current->isTrack( selectedEntry->second))
+	if (current && current->isTrack(selectedEntry->second))
 		return;
 
 	// in this mode - play specific track from set
 	queueNext(this, "", selectedEntry->second, loop);
 }
 
-void CMusicHandler::queueNext(unique_ptr<MusicEntry> queued)
+void CMusicHandler::queueNext(std::unique_ptr<MusicEntry> queued)
 {
 	if (!initialized)
 		return;
@@ -421,12 +422,11 @@ void CMusicHandler::musicFinishedCallback(void)
 MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped):
 	owner(owner),
 	music(nullptr),
-    musicFile(nullptr),
 	loop(looped ? -1 : 1),
-    setName(setName)
+    setName(std::move(setName))
 {
 	if (!musicURI.empty())
-		load(musicURI);
+		load(std::move(musicURI));
 }
 MusicEntry::~MusicEntry()
 {
@@ -448,33 +448,15 @@ void MusicEntry::load(std::string musicURI)
 
 	logGlobal->traceStream()<<"Loading music file "<<musicURI;
 
-	data = CResourceHandler::get()->load(ResourceID(musicURI, EResType::MUSIC))->readAll();
-	musicFile = SDL_RWFromConstMem(data.first.get(), data.second);
-	
-	#ifdef VCMI_SDL1
-	music = Mix_LoadMUS_RW(musicFile);
+	auto musicFile = MakeSDLRWops(CResourceHandler::get()->load(ResourceID(std::move(musicURI), EResType::MUSIC)));
 
-	if(!music)
-	{
-		SDL_FreeRW(musicFile);
-		musicFile = nullptr;
-		logGlobal->warnStream() << "Warning: Cannot open " << currentName << ": " << Mix_GetError();
-		return;
-	}
-
-	#else
-	music = Mix_LoadMUS_RW(musicFile, SDL_FALSE);
+	music = Mix_LoadMUS_RW(musicFile, SDL_TRUE);
 
 	if(!music)
 	{
-		SDL_FreeRW(musicFile);
-		musicFile = nullptr;
 		logGlobal->warnStream() << "Warning: Cannot open " << currentName << ": " << Mix_GetError();
 		return;
 	}
-
-	#endif // 0
-
 }
 
 bool MusicEntry::play()
@@ -488,10 +470,10 @@ bool MusicEntry::play()
 		load(RandomGeneratorUtil::nextItem(set, CRandomGenerator::getDefault())->second);
 	}
 
-    logGlobal->traceStream()<<"Playing music file "<<currentName;
+	logGlobal->traceStream()<<"Playing music file "<<currentName;
 	if(Mix_PlayMusic(music, 1) == -1)
 	{
-        logGlobal->errorStream() << "Unable to play music (" << Mix_GetError() << ")";
+		logGlobal->errorStream() << "Unable to play music (" << Mix_GetError() << ")";
 		return false;
 	}
 	return true;
@@ -501,7 +483,7 @@ bool MusicEntry::stop(int fade_ms)
 {
 	if (Mix_PlayingMusic())
 	{
-        logGlobal->traceStream()<<"Stoping music file "<<currentName;
+		logGlobal->traceStream()<<"Stoping music file "<<currentName;
 		loop = 0;
 		Mix_FadeOutMusic(fade_ms);
 		return true;

+ 12 - 13
client/CMusicHandler.h

@@ -41,7 +41,8 @@ private:
 	SettingsListener listener;
 	void onVolumeChange(const JsonNode &volumeNode);
 
-	std::map<std::string, Mix_Chunk *> soundChunks;
+	using CachedChunk = std::pair<Mix_Chunk *, std::unique_ptr<ui8[]>>;
+	std::map<std::string, CachedChunk> soundChunks;
 
 	Mix_Chunk *GetSoundChunk(std::string &sound, bool cache);
 
@@ -52,10 +53,10 @@ private:
 public:
 	CSoundHandler();
 
-	void init();
-	void release();
+	void init() override;
+	void release() override;
 
-	void setVolume(ui32 percent);
+	void setVolume(ui32 percent) override;
 
 	// Sounds
 	int playSound(soundBase::soundID soundID, int repeats=0);
@@ -80,10 +81,8 @@ class CMusicHandler;
 //Class for handling one music file
 class MusicEntry
 {
-	std::pair<std::unique_ptr<ui8[]>, size_t> data;
 	CMusicHandler *owner;
 	Mix_Music *music;
-	SDL_RWops *musicFile;
 
 	int loop; // -1 = indefinite
 	//if not null - set from which music will be randomly selected
@@ -114,11 +113,11 @@ private:
 	SettingsListener listener;
 	void onVolumeChange(const JsonNode &volumeNode);
 
-	unique_ptr<MusicEntry> current;
-	unique_ptr<MusicEntry> next;
-	
+	std::unique_ptr<MusicEntry> current;
+	std::unique_ptr<MusicEntry> next;
+
 	void queueNext(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped);
-	void queueNext(unique_ptr<MusicEntry> queued);
+	void queueNext(std::unique_ptr<MusicEntry> queued);
 
 	std::map<std::string, std::map<int, std::string> > musicsSet;
 public:
@@ -127,9 +126,9 @@ public:
 	/// add entry with URI musicURI in set. Track will have ID musicID
 	void addEntryToSet(std::string set, int musicID, std::string musicURI);
 
-	void init();
-	void release();
-	void setVolume(ui32 percent);
+	void init() override;
+	void release() override;
+	void setVolume(ui32 percent) override;
 
 	/// play track by URI, if loop = true music will be looped
 	void playMusic(std::string musicURI, bool loop);

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 187 - 169
client/CPlayerInterface.cpp


+ 21 - 29
client/CPlayerInterface.h

@@ -1,12 +1,10 @@
 #pragma once
 
 
-//#include "../lib/CondSh.h"
 #include "../lib/FunctionList.h"
 #include "../lib/CGameInterface.h"
 #include "../lib/NetPacksBase.h"
 #include "gui/CIntObject.h"
-//#include "../lib/CGameState.h"
 
 #ifdef __GNUC__
 #define sprintf_s snprintf
@@ -29,12 +27,9 @@
  *
  */
 
-class CDefEssential;
 class CButton;
 class CToggleGroup;
-class CDefHandler;
 struct TryMoveHero;
-class CDefEssential;
 class CGHeroInstance;
 class CAdvMapInt;
 class CCastleInterface;
@@ -76,20 +71,22 @@ enum
 {
 	/*CHANGE_SCREEN_RESOLUTION = 1,*/
 	RETURN_TO_MAIN_MENU = 2,
-	STOP_CLIENT = 3,
-	RESTART_GAME,
+	//STOP_CLIENT = 3,
+	RESTART_GAME = 4,
 	RETURN_TO_MENU_LOAD,
 	FULLSCREEN_TOGGLED,
-	PREPARE_RESTART_CAMPAIGN
+	PREPARE_RESTART_CAMPAIGN,
+	FORCE_QUIT //quit client without question
 };
 
 /// Central class for managing user interface logic
-class CPlayerInterface : public CGameInterface, public ILockedUpdatable
+class CPlayerInterface : public CGameInterface, public IUpdateable
 {
 	const CArmedInstance * currentSelection;
 public:
 	bool observerInDuelMode;
 	ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation
+	int3 destinationTeleportPos;
 
 	//minor interfaces
 	CondSh<bool> *showingDialog; //indicates if dialog box is displayed
@@ -105,7 +102,7 @@ public:
 	static CBattleInterface * battleInt; //nullptr if no battle
 	CInGameConsole * cingconsole;
 
-	shared_ptr<CCallback> cb; //to communicate with engine
+	std::shared_ptr<CCallback> cb; //to communicate with engine
 	const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr)
 
 	std::list<CInfoWindow *> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
@@ -116,7 +113,7 @@ public:
 	std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
 
 	//During battle is quick combat mode is used
-	shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
+	std::shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
 	bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
 
 	const CArmedInstance * getSelection();
@@ -135,7 +132,6 @@ public:
 	} spellbookSettings;
 
 	void update() override;
-	void runLocked(std::function<void()> functor) override;
 	void initializeHeroTownList();
 	int getLastIndex(std::string namePrefix);
 
@@ -148,11 +144,11 @@ public:
 	void newStackInserted(const StackLocation &location, const CStackInstance &stack) override; //new stack inserted at given (previously empty position)
 	void stacksRebalanced(const StackLocation &src, const StackLocation &dst, TQuantity count) override; //moves creatures from src stack to dst slot, may be used for merging/splittint/moving stacks
 
-	void artifactPut(const ArtifactLocation &al);
-	void artifactRemoved(const ArtifactLocation &al);
-	void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst);
-	void artifactAssembled(const ArtifactLocation &al);
-	void artifactDisassembled(const ArtifactLocation &al);
+	void artifactPut(const ArtifactLocation &al) override;
+	void artifactRemoved(const ArtifactLocation &al) override;
+	void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) override;
+	void artifactAssembled(const ArtifactLocation &al) override;
+	void artifactDisassembled(const ArtifactLocation &al) override;
 
 	void heroCreated(const CGHeroInstance* hero) override;
 	void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
@@ -169,7 +165,7 @@ public:
 	void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override;
 	void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
-	void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
+	void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
 	void showPuzzleMap() override;
 	void viewWorldMap() override;
@@ -196,9 +192,9 @@ public:
 	void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
 	void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface
 	void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox
-	void saveGame(COSer & h, const int version) override; //saving
-	void loadGame(CISer & h, const int version) override; //loading
-	void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;	
+	void saveGame(BinarySerializer & h, const int version) override; //saving
+	void loadGame(BinaryDeserializer & h, const int version) override; //loading
+	void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
 
 	//for battles
 	void actionFinished(const BattleAction& action) override;//occurs AFTER action taken by active stack or by the hero
@@ -221,6 +217,7 @@ public:
 	void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
 	void battleStacksRemoved(const BattleStacksRemoved & bsr) override; //called when certain stack is completely removed from battlefield
 	void battleObstaclePlaced(const CObstacleInstance &obstacle) override;
+	void battleGateStateChanged(const EGateState state) override;
 	void yourTacticPhase(int distance) override;
 
 	//-------------//
@@ -237,7 +234,7 @@ public:
 	void openTownWindow(const CGTownInstance * town); //shows townscreen
 	void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
 	void updateInfo(const CGObjectInstance * specific);
-	void init(shared_ptr<CCallback> CB);
+	void init(std::shared_ptr<CCallback> CB) override;
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
 
 	// show dialogs
@@ -260,7 +257,6 @@ public:
 	void tryDiggging(const CGHeroInstance *h);
 	void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard;
 	void requestReturningToMainMenu();
-	void requestStoppingClient();
 	void sendCustomEvent(int code);
 	void proposeLoadingGame();
 
@@ -270,10 +266,6 @@ public:
 	CPlayerInterface(PlayerColor Player);//c-tor
 	~CPlayerInterface();//d-tor
 
-	static CondSh<bool> terminate_cond; // confirm termination
-
-
-
 private:
 
 	template <typename Handler> void serializeTempl(Handler &h, const int version);
@@ -299,9 +291,9 @@ private:
 	bool duringMovement;
 	bool ignoreEvents;
 
-	bool locked;
-
 	void doMoveHero(const CGHeroInstance *h, CGPath path);
+	void setMovementStatus(bool value);
+	void askToAssembleArtifact(const ArtifactLocation &al);
 };
 
 extern CPlayerInterface * LOCPLINT;

+ 139 - 119
client/CPreGame.cpp

@@ -2,14 +2,12 @@
 #include "CPreGame.h"
 
 #include "../lib/filesystem/Filesystem.h"
-#include "../lib/filesystem/CFileInfo.h"
 #include "../lib/filesystem/CCompressedStream.h"
 
 #include "../lib/CStopWatch.h"
 #include "gui/SDL_Extensions.h"
 #include "CGameInfo.h"
 #include "gui/CCursorHandler.h"
-#include "CDefHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/CHeroHandler.h"
@@ -19,7 +17,8 @@
 #include "CMusicHandler.h"
 #include "CVideoHandler.h"
 #include "Graphics.h"
-#include "../lib/Connection.h"
+#include "../lib/serializer/Connection.h"
+#include "../lib/serializer/CTypeList.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/mapping/CMap.h"
 #include "windows/GUIClasses.h"
@@ -45,8 +44,8 @@
 #include "widgets/TextControls.h"
 #include "windows/InfoWindows.h"
 #include "../lib/mapping/CMapService.h"
-#include "../lib/mapping/CMap.h"
 #include "../lib/CRandomGenerator.h"
+#include "../lib/CondSh.h"
 
 /*
  * CPreGame.cpp, part of VCMI engine
@@ -206,7 +205,7 @@ public:
 template <typename T> class CApplyOnPG : public CBaseForPGApply
 {
 public:
-	void applyOnPG(CSelectionScreen *selScr, void *pack) const
+	void applyOnPG(CSelectionScreen *selScr, void *pack) const override
 	{
 		T *ptr = static_cast<T*>(pack);
 		ptr->apply(selScr);
@@ -216,7 +215,7 @@ public:
 template <> class CApplyOnPG<CPack> : public CBaseForPGApply
 {
 public:
-	void applyOnPG(CSelectionScreen *selScr, void *pack) const
+	void applyOnPG(CSelectionScreen *selScr, void *pack) const override
 	{
 			logGlobal->errorStream() << "Cannot apply on PG plain CPack!";
 			assert(0);
@@ -306,10 +305,10 @@ void CMenuScreen::switchToTab(size_t index)
 //funciton for std::string -> std::function conversion for main menu
 static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::string> menuType, const std::string &string)
 {
-	static const std::vector<std::string> commandType  = 
+	static const std::vector<std::string> commandType  =
 		{"to", "campaigns", "start", "load", "exit", "highscores"};
 
-	static const std::vector<std::string> gameType = 
+	static const std::vector<std::string> gameType =
 		{"single", "multi", "campaign", "tutorial"};
 
 	std::list<std::string> commands;
@@ -368,7 +367,7 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri
 			}
 		}
 	}
-    logGlobal->errorStream()<<"Failed to parse command: "<<string;
+	logGlobal->errorStream()<<"Failed to parse command: "<<string;
 	return std::function<void()>();
 }
 
@@ -504,19 +503,20 @@ void CGPreGame::loadGraphics()
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	new CFilledTexture("DIBOXBCK", pos);
 
-	victory = CDefHandler::giveDef("SCNRVICT.DEF");
-	loss = CDefHandler::giveDef("SCNRLOSS.DEF");
+	victoryIcons = std::make_shared<CAnimation>("SCNRVICT.DEF");
+	victoryIcons->load();
+	lossIcons = std::make_shared<CAnimation>("SCNRLOSS.DEF");
+	lossIcons->load();
 }
 
 void CGPreGame::disposeGraphics()
 {
-	delete victory;
-	delete loss;
+	victoryIcons->unload();
+	lossIcons->unload();
 }
 
 void CGPreGame::update()
 {
-	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
 	if(CGP != this) //don't update if you are not a main interface
 		return;
 
@@ -538,15 +538,6 @@ void CGPreGame::update()
 	// /FIXME: find out why GH.listInt is empty to begin with
 	if (GH.topInt() != nullptr)
 		GH.topInt()->show(screen);
-
-	if (settings["general"]["showfps"].Bool())
-		GH.drawFPSCounter();
-}
-
-void CGPreGame::runLocked(std::function<void()> cb)
-{
-	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
-	cb();	
 }
 
 void CGPreGame::openCampaignScreen(std::string name)
@@ -563,6 +554,8 @@ CGPreGame *CGPreGame::create()
 {
 	if(!CGP)
 		CGP = new CGPreGame();
+
+	GH.terminate_cond.set(false);
 	return CGP;
 }
 
@@ -708,7 +701,10 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		if(isHost())
 		{
 			assert(playerNames.size() == 1  &&  vstd::contains(playerNames, 1)); //TODO hot-seat/network combo
-			serv = sh->connectToServer();
+			if(CServerHandler::DO_NOT_START_SERVER)
+				serv = CServerHandler::justConnectToServer(Address, Port);
+			else
+				serv = sh->connectToServer();
 			*serv << (ui8) 4;
 			myNameID = 1;
 		}
@@ -949,7 +945,7 @@ void CSelectionScreen::handleConnection()
 		{
 			CPackForSelectionScreen *pack = nullptr;
 			*serv >> pack;
-            logNetwork->traceStream() << "Received a pack of type " << typeid(*pack).name();
+			logNetwork->traceStream() << "Received a pack of type " << typeid(*pack).name();
 			assert(pack);
 			if(QuitMenuWithoutStarting *endingPack = dynamic_cast<QuitMenuWithoutStarting *>(pack))
 			{
@@ -1012,7 +1008,7 @@ void CSelectionScreen::processPacks()
 	{
 		CPackForSelectionScreen *pack = upcomingPacks.front();
 		upcomingPacks.pop_front();
-		CBaseForPGApply *apply = applier->apps[typeList.getTypeID(pack)]; //find the applier
+		CBaseForPGApply *apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier
 		apply->applyOnPG(this, pack);
 		delete pack;
 	}
@@ -1105,6 +1101,10 @@ void SelectionTab::filter( int size, bool selectFirst )
 std::unordered_set<ResourceID> SelectionTab::getFiles(std::string dirURI, int resType)
 {
 	boost::to_upper(dirURI);
+	CResourceHandler::get()->updateFilteredFiles([&](const std::string & mount)
+	{
+		return boost::algorithm::starts_with(mount, dirURI);
+	});
 
 	std::unordered_set<ResourceID> ret = CResourceHandler::get()->getFilteredFiles([&](const ResourceID & ident)
 	{
@@ -1117,6 +1117,7 @@ std::unordered_set<ResourceID> SelectionTab::getFiles(std::string dirURI, int re
 
 void SelectionTab::parseMaps(const std::unordered_set<ResourceID> &files)
 {
+	logGlobal->debug("Parsing %d maps", files.size());
 	allItems.clear();
 	for(auto & file : files)
 	{
@@ -1125,13 +1126,14 @@ void SelectionTab::parseMaps(const std::unordered_set<ResourceID> &files)
 			CMapInfo mapInfo;
 			mapInfo.mapInit(file.getName());
 
-			// ignore unsupported map versions (e.g. WoG maps without WoG
-			if (mapInfo.mapHeader->version <= CGI->modh->settings.data["textData"]["mapVersion"].Float())
+			// ignore unsupported map versions (e.g. WoG maps without WoG)
+			// but accept VCMI maps
+			if((mapInfo.mapHeader->version >= EMapFormat::VCMI) || (mapInfo.mapHeader->version <= CGI->modh->settings.data["textData"]["mapVersion"].Float()))
 				allItems.push_back(std::move(mapInfo));
 		}
 		catch(std::exception & e)
 		{
-            logGlobal->errorStream() << "Map " << file.getName() << " is invalid. Message: " << e.what();
+			logGlobal->errorStream() << "Map " << file.getName() << " is invalid. Message: " << e.what();
 		}
 	}
 }
@@ -1142,7 +1144,7 @@ void SelectionTab::parseGames(const std::unordered_set<ResourceID> &files, bool
 	{
 		try
 		{
-			CLoadFile lf(*CResourceHandler::get()->getResourceName(file), minSupportedVersion);
+			CLoadFile lf(*CResourceHandler::get()->getResourceName(file), MINIMAL_SERIALIZATION_VERSION);
 			lf.checkMagicBytes(SAVEGAME_MAGIC);
 // 			ui8 sign[8];
 // 			lf >> sign;
@@ -1154,11 +1156,11 @@ void SelectionTab::parseGames(const std::unordered_set<ResourceID> &files, bool
 			// Create the map info object
 			CMapInfo mapInfo;
 			mapInfo.mapHeader = make_unique<CMapHeader>();
-			mapInfo.scenarioOpts = new StartInfo;
+			mapInfo.scenarioOpts = nullptr;//to be created by serialiser
 			lf >> *(mapInfo.mapHeader.get()) >> mapInfo.scenarioOpts;
 			mapInfo.fileURI = file.getName();
 			mapInfo.countPlayers();
-			std::time_t time = CFileInfo(*CResourceHandler::get()->getResourceName(file)).getDate();
+			std::time_t time = boost::filesystem::last_write_time(*CResourceHandler::get()->getResourceName(file));
 			mapInfo.date = std::asctime(std::localtime(&time));
 
 			// If multi mode then only multi games, otherwise single
@@ -1171,7 +1173,7 @@ void SelectionTab::parseGames(const std::unordered_set<ResourceID> &files, bool
 		}
 		catch(const std::exception & e)
 		{
-            logGlobal->errorStream() << "Error: Failed to process " << file.getName() <<": " << e.what();
+			logGlobal->errorStream() << "Error: Failed to process " << file.getName() <<": " << e.what();
 		}
 	}
 }
@@ -1291,7 +1293,9 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const std::function<void(CM
 
 	slider = new CSlider(Point(372, 86), tabType != CMenuScreen::saveGame ? 480 : 430, std::bind(&SelectionTab::sliderMove, this, _1), positions, curItems.size(), 0, false, CSlider::BLUE);
 	slider->addUsedEvents(WHEEL);
-	format =  CDefHandler::giveDef("SCSELC.DEF");
+
+	formatIcons = std::make_shared<CAnimation>("SCSELC.DEF");
+	formatIcons->load();
 
 	sortingBy = _format;
 	ascending = true;
@@ -1320,7 +1324,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const std::function<void(CM
 
 SelectionTab::~SelectionTab()
 {
-	delete format;
+	formatIcons->unload();
 }
 
 void SelectionTab::sortBy( int criteria )
@@ -1369,9 +1373,9 @@ void SelectionTab::select( int position )
 
 	if(txt)
 	{
-		std::string filename = *CResourceHandler::get("local")->getResourceName(
+		auto filename = *CResourceHandler::get("local")->getResourceName(
 								   ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));
-		txt->setText(CFileInfo(filename).getBaseName());
+		txt->setText(filename.stem().string());
 	}
 
 	onSelect(curItems[py]);
@@ -1445,33 +1449,51 @@ void SelectionTab::printMaps(SDL_Surface *to)
 			}
 			printAtMiddleLoc(temp2, 70, 128 + line * 25, FONT_SMALL, itemColor, to);
 
-			int temp=-1;
+			int frame = -1, group = 0;
 			switch (currentItem->mapHeader->version)
 			{
 			case EMapFormat::ROE:
-				temp=0;
+				frame = 0;
 				break;
 			case EMapFormat::AB:
-				temp=1;
+				frame = 1;
 				break;
 			case EMapFormat::SOD:
-				temp=2;
+				frame = 2;
 				break;
 			case EMapFormat::WOG:
-				temp=3;
+				frame = 3;
+				break;
+			case EMapFormat::VCMI:
+				frame = 0;
+				group = 1;
 				break;
 			default:
 				// Unknown version. Be safe and ignore that map
-                logGlobal->warnStream() << "Warning: " << currentItem->fileURI << " has wrong version!";
+				logGlobal->warnStream() << "Warning: " << currentItem->fileURI << " has wrong version!";
 				continue;
 			}
-			blitAtLoc(format->ourImages[temp].bitmap, 88, 117 + line * 25, to);
+			IImage * icon = formatIcons->getImage(frame,group);
+			if(icon)
+			{
+				icon->draw(to, pos.x + 88, pos.y + 117 + line * 25);
+				icon->decreaseRef();
+			}
 
 			//victory conditions
-			blitAtLoc(CGP->victory->ourImages[currentItem->mapHeader->victoryIconIndex].bitmap, 306, 117 + line * 25, to);
-
+			icon = CGP->victoryIcons->getImage(currentItem->mapHeader->victoryIconIndex,0);
+			if(icon)
+			{
+				icon->draw(to, pos.x + 306, pos.y + 117 + line * 25);
+				icon->decreaseRef();
+			}
 			//loss conditions
-			blitAtLoc(CGP->loss->ourImages[currentItem->mapHeader->defeatIconIndex].bitmap, 339, 117 + line * 25, to);
+			icon = CGP->lossIcons->getImage(currentItem->mapHeader->defeatIconIndex,0);
+			if(icon)
+			{
+				icon->draw(to, pos.x + 339, pos.y + 117 + line * 25);
+				icon->decreaseRef();
+			}
 		}
 		else //if campaign
 		{
@@ -1494,8 +1516,8 @@ void SelectionTab::printMaps(SDL_Surface *to)
 		}
 		else
 		{
-			name = CFileInfo(*CResourceHandler::get("local")->getResourceName(
-								 ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName();
+			name = CResourceHandler::get("local")->getResourceName(
+								 ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))->stem().string();
 		}
 
 		//print name
@@ -1671,7 +1693,7 @@ CRandomMapTab::CRandomMapTab()
 	{
 		mapGenOptions.setPlayerCount(btnId);
 		deactivateButtonsFrom(teamsCntGroup, btnId);
-		deactivateButtonsFrom(compOnlyPlayersCntGroup, 8 - btnId + 1);
+		deactivateButtonsFrom(compOnlyPlayersCntGroup, btnId);
 		validatePlayersCnt(btnId);
 		if(!SEL->isGuest())
 			updateMapInfo();
@@ -1694,7 +1716,6 @@ CRandomMapTab::CRandomMapTab()
 	compOnlyPlayersCntGroup->pos.y += 285;
 	compOnlyPlayersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
-	compOnlyPlayersCntGroup->setSelected(0);
 	compOnlyPlayersCntGroup->addCallback([&](int btnId)
 	{
 		mapGenOptions.setCompOnlyPlayerCount(btnId);
@@ -1808,9 +1829,9 @@ void CRandomMapTab::validatePlayersCnt(int playersCnt)
 		mapGenOptions.setTeamCount(playersCnt - 1);
 		teamsCntGroup->setSelected(mapGenOptions.getTeamCount());
 	}
-	if(mapGenOptions.getCompOnlyPlayerCount() > 8 - playersCnt)
+	if(mapGenOptions.getCompOnlyPlayerCount() >= playersCnt)
 	{
-		mapGenOptions.setCompOnlyPlayerCount(8 - playersCnt);
+		mapGenOptions.setCompOnlyPlayerCount(playersCnt - 1);
 		compOnlyPlayersCntGroup->setSelected(mapGenOptions.getCompOnlyPlayerCount());
 	}
 
@@ -1883,9 +1904,9 @@ void CRandomMapTab::updateMapInfo()
 
 	// Generate player information
 	mapInfo->mapHeader->players.clear();
-	int playersToGen = (mapGenOptions.getPlayerCount() == CMapGenOptions::RANDOM_SIZE
-		|| mapGenOptions.getCompOnlyPlayerCount() == CMapGenOptions::RANDOM_SIZE)
-			? 8 : mapGenOptions.getPlayerCount() + mapGenOptions.getCompOnlyPlayerCount();
+	int playersToGen = PlayerColor::PLAYER_LIMIT_I;
+	if(mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE)
+		playersToGen = mapGenOptions.getPlayerCount();
 	mapInfo->mapHeader->howManyTeams = playersToGen;
 
 	for(int i = 0; i < playersToGen; ++i)
@@ -1893,7 +1914,8 @@ void CRandomMapTab::updateMapInfo()
 		PlayerInfo player;
 		player.isFactionRandom = true;
 		player.canComputerPlay = true;
-		if(i >= mapGenOptions.getPlayerCount() && mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE)
+		if(mapGenOptions.getCompOnlyPlayerCount() != CMapGenOptions::RANDOM_SIZE &&
+			i >= mapGenOptions.getHumanOnlyPlayerCount())
 		{
 			player.canHumanPlay = false;
 		}
@@ -1925,7 +1947,7 @@ const CMapGenOptions & CRandomMapTab::getMapGenOptions() const
 	return mapGenOptions;
 }
 
-void CRandomMapTab::setMapGenOptions(shared_ptr<CMapGenOptions> opts)
+void CRandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
 {
 	mapSizeBtnGroup->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth()));
 	twoLevelsBtn->setSelected(opts->getHasTwoLevels());
@@ -1973,8 +1995,8 @@ void CChatBox::addNewMessage(const std::string &text)
 }
 
 InfoCard::InfoCard( bool Network )
-  : bg(nullptr), network(Network), chatOn(false), chat(nullptr), playerListBg(nullptr),
-	difficulty(nullptr), sizes(nullptr), sFlags(nullptr)
+  : sizes(nullptr), bg(nullptr), network(Network), chatOn(false), chat(nullptr), playerListBg(nullptr),
+	difficulty(nullptr)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	CIntObject::type |= REDRAW_PARENT;
@@ -2000,8 +2022,11 @@ InfoCard::InfoCard( bool Network )
 		parent->children.pop_back();
 		pos.w = bg->pos.w;
 		pos.h = bg->pos.h;
-		sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");
-		sFlags = CDefHandler::giveDef("ITGFLAGS.DEF");
+		sizes = new CAnimImage("SCNRMPSZ", 4, 0, 318, 22);//let it be custom size (frame 4) by default
+		sizes->recActions &= ~(SHOWALL | UPDATE);//explicit draw
+
+		sFlags = std::make_shared<CAnimation>("ITGFLAGS.DEF");
+		sFlags->load();
 		difficulty = new CToggleGroup(0);
 		{
 			static const char *difButns[] = {"GSPBUT3.DEF", "GSPBUT4.DEF", "GSPBUT5.DEF", "GSPBUT6.DEF", "GSPBUT7.DEF"};
@@ -2026,12 +2051,16 @@ InfoCard::InfoCard( bool Network )
 		}
 	}
 
+	victory = new CAnimImage("SCNRVICT",0, 0, 24, 302);
+	victory->recActions &= ~(SHOWALL | UPDATE);//explicit draw
+	loss = new CAnimImage("SCNRLOSS", 0, 0, 24, 359);
+	loss->recActions &= ~(SHOWALL | UPDATE);//explicit draw
 }
 
 InfoCard::~InfoCard()
 {
-	delete sizes;
-	delete sFlags;
+	if(sFlags)
+		sFlags->unload();
 }
 
 void InfoCard::showAll(SDL_Surface * to)
@@ -2079,27 +2108,17 @@ void InfoCard::showAll(SDL_Surface * to)
 	{
 		if(SEL->screenType != CMenuScreen::campaignList)
 		{
-			int temp = -1;
-
 			if(!chatOn)
 			{
-				CDefHandler * loss    = CGP ? CGP->loss    : CDefHandler::giveDef("SCNRLOSS.DEF");
-				CDefHandler * victory = CGP ? CGP->victory : CDefHandler::giveDef("SCNRVICT.DEF");
-
 				CMapHeader * header = SEL->current->mapHeader.get();
 				//victory conditions
 				printAtLoc(header->victoryMessage, 60, 307, FONT_SMALL, Colors::WHITE, to);
-				blitAtLoc(victory->ourImages[header->victoryIconIndex].bitmap, 24, 302, to); //victory cond descr
-
+				victory->setFrame(header->victoryIconIndex);
+				victory->showAll(to);
 				//loss conditoins
 				printAtLoc(header->defeatMessage, 60, 366, FONT_SMALL, Colors::WHITE, to);
-				blitAtLoc(loss->ourImages[header->defeatIconIndex].bitmap, 24, 359, to); //loss cond
-
-				if (!CGP)
-				{
-					delete loss;
-					delete victory;
-				}
+				loss->setFrame(header->defeatIconIndex);
+				loss->showAll(to);
 			}
 
 			//difficulty
@@ -2111,23 +2130,22 @@ void InfoCard::showAll(SDL_Surface * to)
 			switch (SEL->current->mapHeader->width)
 			{
 			case 36:
-				temp=0;
+				sizes->setFrame(0);
 				break;
 			case 72:
-				temp=1;
+				sizes->setFrame(1);
 				break;
 			case 108:
-				temp=2;
+				sizes->setFrame(2);
 				break;
 			case 144:
-				temp=3;
+				sizes->setFrame(3);
 				break;
 			default:
-				temp=4;
+				sizes->setFrame(4);
 				break;
 			}
-			blitAtLoc(sizes->ourImages[temp].bitmap, 318, 22, to);
-
+			sizes->showAll(to);
 
 			if(SEL->screenType == CMenuScreen::loadGame)
 				printToLoc((static_cast<const CMapInfo*>(SEL->current))->date,308,34, FONT_SMALL, Colors::WHITE, to);
@@ -2146,8 +2164,10 @@ void InfoCard::showAll(SDL_Surface * to)
 			for (auto i = SEL->sInfo.playerInfos.cbegin(); i != SEL->sInfo.playerInfos.cend(); i++)
 			{
 				int *myx = ((i->first == playerColor  ||  SEL->current->mapHeader->players[i->first.getNum()].team == myT) ? &fx : &ex);
-				blitAtLoc(sFlags->ourImages[i->first.getNum()].bitmap, *myx, 399, to);
-				*myx += sFlags->ourImages[i->first.getNum()].bitmap->w;
+				IImage * flag = sFlags->getImage(i->first.getNum(),0);
+				flag->draw(to, pos.x + *myx, pos.y + 399);
+				*myx += flag->width();
+				flag->decreaseRef();
 			}
 
 			std::string tob;
@@ -2242,7 +2262,9 @@ void InfoCard::showTeamsPopup()
 		int curx = 128 - 9*flags.size();
 		for(auto & flag : flags)
 		{
-			blitAt(sFlags->ourImages[flag].bitmap, curx, 75 + 50*i, bmp);
+			IImage * icon = sFlags->getImage(flag,0);
+			icon->draw(bmp, curx, 75 + 50*i);
+			icon->decreaseRef();
 			curx += 18;
 		}
 	}
@@ -3124,14 +3146,17 @@ void CMultiMode::hostTCP()
 	Settings name = settings.write["general"]["playerName"];
 	name->String() = txt->text;
 	GH.popIntTotally(this);
-	GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, CMenuScreen::MULTI_NETWORK_HOST));
+	if(CServerHandler::DO_NOT_START_SERVER)
+		GH.pushInt(new CSimpleJoinScreen(CMenuScreen::MULTI_NETWORK_HOST));
+	else
+		GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, CMenuScreen::MULTI_NETWORK_HOST));
 }
 
 void CMultiMode::joinTCP()
 {
 	Settings name = settings.write["general"]["playerName"];
 	name->String() = txt->text;
-	GH.pushInt(new CSimpleJoinScreen);
+	GH.pushInt(new CSimpleJoinScreen(CMenuScreen::MULTI_NETWORK_GUEST));
 }
 
 CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer)
@@ -3226,7 +3251,8 @@ void CBonusSelection::init()
 		graphics->fonts[FONT_BIG]->renderTextLeft(background, CGI->generaltexth->allTexts[508], Colors::YELLOW, Point(481, 28));
 
 	//map size icon
-	sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");
+	sizes = new CAnimImage("SCNRMPSZ",4,0,735, 26);
+	sizes->recActions &= ~(SHOWALL | UPDATE);//explicit draw
 
 	//campaign description
 	graphics->fonts[FONT_SMALL]->renderTextLeft(background, CGI->generaltexth->allTexts[38], Colors::YELLOW, Point(481, 63));
@@ -3277,14 +3303,10 @@ void CBonusSelection::init()
 	graphics->fonts[FONT_MEDIUM]->renderTextLeft(background, difficulty.back(), Colors::WHITE, Point(689, 432));
 
 	//difficulty pics
-	for (int b=0; b<ARRAY_COUNT(diffPics); ++b)
+	for (size_t b=0; b < diffPics.size(); ++b)
 	{
-		CDefEssential * cde = CDefHandler::giveDefEss("GSPBUT" + boost::lexical_cast<std::string>(b+3) + ".DEF");
-		SDL_Surface * surfToDuplicate = cde->ourImages[0].bitmap;
-		diffPics[b] = SDL_ConvertSurface(surfToDuplicate, surfToDuplicate->format,
-			surfToDuplicate->flags);
-
-		delete cde;
+		diffPics[b] = new CAnimImage("GSPBUT" + boost::lexical_cast<std::string>(b+3) + ".DEF", 0, 0, 709, 455);
+		diffPics[b]->recActions &= ~(SHOWALL | UPDATE);//explicit draw
 	}
 
 	//difficulty selection buttons
@@ -3295,30 +3317,26 @@ void CBonusSelection::init()
 	}
 
 	//load miniflags
-	sFlags = CDefHandler::giveDef("ITGFLAGS.DEF");
+	sFlags = std::make_shared<CAnimation>("ITGFLAGS.DEF");
+	sFlags->load();
 }
 
-CBonusSelection::CBonusSelection(shared_ptr<CCampaignState> _ourCampaign) : ourCampaign(_ourCampaign)
+CBonusSelection::CBonusSelection(std::shared_ptr<CCampaignState> _ourCampaign) : ourCampaign(_ourCampaign)
 {
 	init();
 }
 
 CBonusSelection::CBonusSelection(const std::string & campaignFName)
 {
-	ourCampaign = make_shared<CCampaignState>(CCampaignHandler::getCampaign(campaignFName));
+	ourCampaign = std::make_shared<CCampaignState>(CCampaignHandler::getCampaign(campaignFName));
 	init();
 }
 
 CBonusSelection::~CBonusSelection()
 {
 	SDL_FreeSurface(background);
-	delete sizes;
 	delete ourHeader;
-	delete sFlags;
-	for (auto & elem : diffPics)
-	{
-		SDL_FreeSurface(elem);
-	}
+	sFlags->unload();
 }
 
 void CBonusSelection::goBack()
@@ -3417,8 +3435,6 @@ void CBonusSelection::selectMap(int mapNr, bool initialSelect)
 
 void CBonusSelection::show(SDL_Surface * to)
 {
-	//blitAt(background, pos.x, pos.y, to);
-
 	//map name
 	std::string mapName = ourHeader->name;
 
@@ -3452,7 +3468,8 @@ void CBonusSelection::show(SDL_Surface * to)
 		temp=4;
 		break;
 	}
-	blitAtLoc(sizes->ourImages[temp].bitmap, 735, 26, to);
+	sizes->setFrame(temp);
+	sizes->showAll(to);
 
 	//flags
 	int fx = 496  + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[390]);
@@ -3462,12 +3479,15 @@ void CBonusSelection::show(SDL_Surface * to)
 	for (auto i = startInfo.playerInfos.cbegin(); i != startInfo.playerInfos.cend(); i++)
 	{
 		int *myx = ((i->first == playerColor  ||  ourHeader->players[i->first.getNum()].team == myT) ? &fx : &ex);
-		blitAtLoc(sFlags->ourImages[i->first.getNum()].bitmap, *myx, 405, to);
-		*myx += sFlags->ourImages[i->first.getNum()].bitmap->w;
+
+		IImage * flag = sFlags->getImage(i->first.getNum(),0);
+		flag->draw(to, pos.x + *myx, pos.y + 405);
+		*myx += flag->width();
+		flag->decreaseRef();
 	}
 
 	//difficulty
-	blitAtLoc(diffPics[startInfo.difficulty], 709, 455, to);
+	diffPics[startInfo.difficulty]->showAll(to);
 
 	CIntObject::show(to);
 }
@@ -3651,7 +3671,7 @@ void CBonusSelection::updateBonusSelection()
 		if (picNumber != -1)
 			picName += ":" + boost::lexical_cast<std::string>(picNumber);
 
-		auto   anim = new CAnimation();
+		auto anim = std::make_shared<CAnimation>();
 		anim->setCustom(picName, 0);
 		bonusButton->setImage(anim);
 		const SDL_Color brightYellow = { 242, 226, 110, 0 };
@@ -4268,7 +4288,7 @@ void CPrologEpilogVideo::clickLeft( tribool down, bool previousState )
 	exitCb();
 }
 
-CSimpleJoinScreen::CSimpleJoinScreen()
+CSimpleJoinScreen::CSimpleJoinScreen(CMenuScreen::EMultiMode mode)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	bg = new CPicture("MUDIALOG.bmp"); // address background
@@ -4284,7 +4304,7 @@ CSimpleJoinScreen::CSimpleJoinScreen()
     port->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1);
     port->filters += std::bind(&CTextInput::numberFilter, _1, _2, 0, 65535);
 
-	ok     = new CButton(Point( 26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::enterSelectionScreen, this), SDLK_RETURN);
+	ok     = new CButton(Point( 26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::enterSelectionScreen, this, mode), SDLK_RETURN);
 	cancel = new CButton(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), SDLK_ESCAPE);
 	bar = new CGStatusBar(new CPicture(Rect(7, 186, 218, 18), 0));
 
@@ -4293,15 +4313,15 @@ CSimpleJoinScreen::CSimpleJoinScreen()
 	address->giveFocus();
 }
 
-void CSimpleJoinScreen::enterSelectionScreen()
+void CSimpleJoinScreen::enterSelectionScreen(CMenuScreen::EMultiMode mode)
 {
 	std::string textAddress = address->text;
 	std::string textPort = port->text;
 
 	GH.popIntTotally(this);
-	GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, CMenuScreen::MULTI_NETWORK_GUEST, nullptr, textAddress, textPort));
-}
 
+	GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, mode, nullptr, textAddress, textPort));
+}
 void CSimpleJoinScreen::onChange(const std::string & newText)
 {
 	ok->block(address->text.empty() || port->text.empty());

+ 48 - 45
client/CPreGame.h

@@ -1,6 +1,5 @@
 #pragma once
 
-//#include "../lib/filesystem/Filesystem.h"
 #include "../lib/StartInfo.h"
 #include "../lib/FunctionList.h"
 #include "../lib/mapping/CMapInfo.h"
@@ -36,7 +35,11 @@ class CMultiLineLabel;
 class CToggleButton;
 class CToggleGroup;
 class CTabbedInt;
+class IImage;
+class CAnimation;
+class CAnimImage;
 class CButton;
+class CLabel;
 class CSlider;
 
 namespace boost{ class thread; class recursive_mutex;}
@@ -75,10 +78,10 @@ public:
 	};
 	CMenuScreen(const JsonNode& configNode);
 
-	void showAll(SDL_Surface * to);
-	void show(SDL_Surface * to);
-	void activate();
-	void deactivate();
+	void showAll(SDL_Surface * to) override;
+	void show(SDL_Surface * to) override;
+	void activate() override;
+	void deactivate() override;
 
 	void switchToTab(size_t index);
 };
@@ -100,10 +103,10 @@ class CreditsScreen : public CIntObject
 public:
 	CreditsScreen();
 
-	void show(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
 
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
 };
 
 /// Implementation of the chat box
@@ -115,13 +118,15 @@ public:
 
 	CChatBox(const Rect &rect);
 
-	void keyPressed(const SDL_KeyboardEvent & key);
+	void keyPressed(const SDL_KeyboardEvent & key) override;
 
 	void addNewMessage(const std::string &text);
 };
 
 class InfoCard : public CIntObject
 {
+	CAnimImage * victory, * loss, *sizes;
+	std::shared_ptr<CAnimation> sFlags;
 public:
 	CPicture *bg;
 	CMenuScreen::EState type;
@@ -133,11 +138,10 @@ public:
 	CPicture *playerListBg;
 
 	CToggleGroup *difficulty;
-	CDefHandler *sizes, *sFlags;
 
 	void changeSelection(const CMapInfo *to);
-	void showAll(SDL_Surface * to);
-	void clickRight(tribool down, bool previousState);
+	void showAll(SDL_Surface * to) override;
+	void clickRight(tribool down, bool previousState) override;
 	void showTeamsPopup();
 	void toggleChat();
 	void setChat(bool activateChat);
@@ -149,9 +153,9 @@ public:
 class SelectionTab : public CIntObject
 {
 private:
-	CDefHandler *format; //map size
+	std::shared_ptr<CAnimation> formatIcons;
 
-    void parseMaps(const std::unordered_set<ResourceID> &files);
+	void parseMaps(const std::unordered_set<ResourceID> &files);
 	void parseGames(const std::unordered_set<ResourceID> &files, bool multi);
 	void parseCampaigns(const std::unordered_set<ResourceID> & files );
 	std::unordered_set<ResourceID> getFiles(std::string dirURI, int resType);
@@ -184,10 +188,10 @@ public:
 	void selectFName(std::string fname);
 	const CMapInfo * getSelectedMapInfo() const;
 
-	void showAll(SDL_Surface * to);
-	void clickLeft(tribool down, bool previousState);
-	void keyPressed(const SDL_KeyboardEvent & key);
-	void onDoubleClick();
+	void showAll(SDL_Surface * to) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	void onDoubleClick() override;
 	SelectionTab(CMenuScreen::EState Type, const std::function<void(CMapInfo *)> &OnSelect, CMenuScreen::EMultiMode MultiPlayer = CMenuScreen::SINGLE_PLAYER);
     ~SelectionTab();
 };
@@ -235,7 +239,7 @@ public:
 		CLabel *subtitle;
 
 		SelectedBox(Point position, PlayerSettings & settings, SelType type);
-		void clickRight(tribool down, bool previousState);
+		void clickRight(tribool down, bool previousState) override;
 
 		void update();
 	};
@@ -254,7 +258,7 @@ public:
 
 		PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S);
 		void selectButtons(); //hides unavailable buttons
-		void showAll(SDL_Surface * to);
+		void showAll(SDL_Surface * to) override;
 		void update();
 	};
 
@@ -282,7 +286,7 @@ public:
 	void recreate();
 	OptionsTab();
 	~OptionsTab();
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 
 	int nextAllowedHero(PlayerColor player, int min, int max, int incl, int dir );
 
@@ -295,12 +299,12 @@ class CRandomMapTab : public CIntObject
 public:
 	CRandomMapTab();
 
-    void showAll(SDL_Surface * to);
+    void showAll(SDL_Surface * to) override;
 	void updateMapInfo();
 	CFunctionList<void (const CMapInfo *)> & getMapInfoChanged();
 	const CMapInfo * getMapInfo() const;
 	const CMapGenOptions & getMapGenOptions() const;
-	void setMapGenOptions(shared_ptr<CMapGenOptions> opts);
+	void setMapGenOptions(std::shared_ptr<CMapGenOptions> opts);
 
 private:
     void addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex) const;
@@ -316,7 +320,7 @@ private:
 		* compOnlyTeamsCntGroup, * waterContentGroup, * monsterStrengthGroup;
     CButton * showRandMaps;
 	CMapGenOptions mapGenOptions;
-	unique_ptr<CMapInfo> mapInfo;
+	std::unique_ptr<CMapInfo> mapInfo;
 	CFunctionList<void(const CMapInfo *)> mapInfoChanged;
 };
 
@@ -385,7 +389,7 @@ public:
 	void postRequest(ui8 what, ui8 dir) override;
 	void postChatMessage(const std::string &txt) override;
 	void propagateNames();
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 };
 
 /// Save game screen
@@ -454,8 +458,8 @@ class CPrologEpilogVideo : public CWindowObject
 public:
 	CPrologEpilogVideo(CCampaignScenario::SScenarioPrologEpilog _spe, std::function<void()> callback);
 
-	void clickLeft(tribool down, bool previousState);
-	void show(SDL_Surface * to);
+	void clickLeft(tribool down, bool previousState) override;
+	void show(SDL_Surface * to) override;
 };
 
 /// Campaign screen where you can choose one out of three starting bonuses
@@ -463,7 +467,7 @@ class CBonusSelection : public CIntObject
 {
 public:
 	CBonusSelection(const std::string & campaignFName);
-	CBonusSelection(shared_ptr<CCampaignState> _ourCampaign);
+	CBonusSelection(std::shared_ptr<CCampaignState> _ourCampaign);
 	~CBonusSelection();
 
 	void showAll(SDL_Surface * to) override;
@@ -498,9 +502,9 @@ private:
 		CRegion(CBonusSelection * _owner, bool _accessible, bool _selectable, int _myNumber);
 		~CRegion();
 
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
-		void show(SDL_Surface * to);
+		void clickLeft(tribool down, bool previousState) override;
+		void clickRight(tribool down, bool previousState) override;
+		void show(SDL_Surface * to) override;
 	};
 
 	void init();
@@ -526,13 +530,13 @@ private:
 	std::vector<CRegion *> regions;
 	CRegion * highlightedRegion;
 	CToggleGroup * bonuses;
-	SDL_Surface * diffPics[5]; //pictures of difficulties, user-selectable (or not if campaign locks this)
+	std::array<CAnimImage *, 5> diffPics; //pictures of difficulties, user-selectable (or not if campaign locks this)
 	CButton * diffLb, * diffRb; //buttons for changing difficulty
-	CDefHandler * sizes; //icons of map sizes
-	CDefHandler * sFlags;
+	CAnimImage * sizes;//icons of map sizes
+	std::shared_ptr<CAnimation> sFlags;
 
 	// Data
-	shared_ptr<CCampaignState> ourCampaign;
+	std::shared_ptr<CCampaignState> ourCampaign;
 	int selectedMap;
 	boost::optional<int> selectedBonus;
 	StartInfo startInfo;
@@ -560,12 +564,12 @@ private:
 		std::string video; // the resource name of the video
 		std::string hoverText;
 
-		void clickLeft(tribool down, bool previousState);
-		void hover(bool on);
+		void clickLeft(tribool down, bool previousState) override;
+		void hover(bool on) override;
 
 	public:
 		CCampaignButton(const JsonNode &config );
-		void show(SDL_Surface * to);
+		void show(SDL_Surface * to) override;
 	};
 
 	CButton *back;
@@ -578,7 +582,7 @@ public:
 	enum CampaignSet {ROE, AB, SOD, WOG};
 
 	CCampaignScreen(const JsonNode &config);
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 };
 
 /// Manages the configuration of pregame GUI elements like campaign screen, main menu, loading screen,...
@@ -597,7 +601,7 @@ private:
 };
 
 /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection
-class CGPreGame : public CIntObject, public ILockedUpdatable
+class CGPreGame : public CIntObject, public IUpdateable
 {
 	void loadGraphics();
 	void disposeGraphics();
@@ -607,11 +611,10 @@ class CGPreGame : public CIntObject, public ILockedUpdatable
 public:
 	CMenuScreen * menu;
 
-	CDefHandler *victory, *loss;
+	std::shared_ptr<CAnimation> victoryIcons, lossIcons;
 
 	~CGPreGame();
 	void update() override;
-	void runLocked(std::function<void()> cb) override;
 	void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER);
 
 	void openCampaignScreen(std::string name);
@@ -630,7 +633,7 @@ public:
 	CLoadingScreen(std::function<void()> loader);
 	~CLoadingScreen();
 
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 };
 
 /// Simple window to enter the server's address.
@@ -643,10 +646,10 @@ class CSimpleJoinScreen : public CIntObject
 	CTextInput * address;
 	CTextInput * port;
 
-	void enterSelectionScreen();
+	void enterSelectionScreen(CMenuScreen::EMultiMode mode);
 	void onChange(const std::string & newText);
 public:
-	CSimpleJoinScreen();
+	CSimpleJoinScreen(CMenuScreen::EMultiMode mode);
 };
 
 extern ISelectionScreenInfo *SEL;

+ 11 - 76
client/CVideoHandler.cpp

@@ -53,11 +53,7 @@ CVideoPlayer::CVideoPlayer()
 	frame = nullptr;
 	codec = nullptr;
 	sws = nullptr;
-#ifdef VCMI_SDL1
-	overlay = nullptr;
-#else
 	texture = nullptr;
-#endif
 	dest = nullptr;
 	context = nullptr;
 
@@ -108,11 +104,7 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 		return false;
 	}
 	// Retrieve stream information
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 17, 0)
-	if (av_find_stream_info(format) < 0)
-#else
 	if (avformat_find_stream_info(format, nullptr) < 0)
-#endif
 		return false;
 
 	// Find the first video stream
@@ -143,22 +135,16 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 	}
 
 	// Open codec
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0)
-	if ( avcodec_open(codecContext, codec) < 0 )
-#else
 	if ( avcodec_open2(codecContext, codec, nullptr) < 0 )
-#endif
 	{
 		// Could not open codec
 		codec = nullptr;
 		return false;
 	}
-
 	// Allocate video frame
-	frame = avcodec_alloc_frame();
+	frame = av_frame_alloc();
 	
 	//setup scaling
-	
 	if(scale)
 	{
 		pos.w = screen->w;		
@@ -173,13 +159,7 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 	// Allocate a place to put our YUV image on that screen
 	if (useOverlay)
 	{
-#ifdef VCMI_SDL1
-		overlay = SDL_CreateYUVOverlay(pos.w, pos.h,
-									   SDL_YV12_OVERLAY, screen);
-#else
 		texture = SDL_CreateTexture( mainRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STATIC, pos.w, pos.h);
-#endif
-
 	}
 	else
 	{
@@ -188,34 +168,28 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 		destRect.w = pos.w;
 		destRect.h = pos.h;
 	}
-#ifdef VCMI_SDL1
-	if (overlay == nullptr && dest == nullptr)
-		return false;
 
-	if (overlay)
-#else
 	if (texture == nullptr && dest == nullptr)
 		return false;
 
 	if (texture)
-#endif
 	{ // Convert the image into YUV format that SDL uses
 		sws = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, 
-							 pos.w, pos.h, PIX_FMT_YUV420P, 
+							 pos.w, pos.h,
+							 AV_PIX_FMT_YUV420P,
 							 SWS_BICUBIC, nullptr, nullptr, nullptr);
 	}
 	else
 	{
-
-		PixelFormat screenFormat = PIX_FMT_NONE;
+		AVPixelFormat screenFormat = AV_PIX_FMT_NONE;
 		if (screen->format->Bshift > screen->format->Rshift)
 		{
 			// this a BGR surface
 			switch (screen->format->BytesPerPixel)
 			{
-				case 2: screenFormat = PIX_FMT_BGR565; break;
-				case 3: screenFormat = PIX_FMT_BGR24; break;
-				case 4: screenFormat = PIX_FMT_BGR32; break;
+				case 2: screenFormat = AV_PIX_FMT_BGR565; break;
+				case 3: screenFormat = AV_PIX_FMT_BGR24; break;
+				case 4: screenFormat = AV_PIX_FMT_BGR32; break;
 				default: return false;
 			}
 		}
@@ -224,9 +198,9 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 			// this a RGB surface
 			switch (screen->format->BytesPerPixel)
 			{
-				case 2: screenFormat = PIX_FMT_RGB565; break;
-				case 3: screenFormat = PIX_FMT_RGB24; break;
-				case 4: screenFormat = PIX_FMT_RGB32; break;
+				case 2: screenFormat = AV_PIX_FMT_RGB565; break;
+				case 3: screenFormat = AV_PIX_FMT_RGB24; break;
+				case 4: screenFormat = AV_PIX_FMT_RGB32; break;
 				default: return false;
 			}
 		}
@@ -284,23 +258,6 @@ bool CVideoPlayer::nextFrame()
 				{
 					AVPicture pict;
 
-#ifdef VCMI_SDL1
-					if (overlay) {
-						SDL_LockYUVOverlay(overlay);
-
-						pict.data[0] = overlay->pixels[0];
-						pict.data[1] = overlay->pixels[2];
-						pict.data[2] = overlay->pixels[1];
-
-						pict.linesize[0] = overlay->pitches[0];
-						pict.linesize[1] = overlay->pitches[2];
-						pict.linesize[2] = overlay->pitches[1];
-
-						sws_scale(sws, frame->data, frame->linesize,
-								  0, codecContext->height, pict.data, pict.linesize);
-
-						SDL_UnlockYUVOverlay(overlay);
-#else
 					if (texture) {
 						avpicture_alloc(&pict, AV_PIX_FMT_YUV420P, pos.w, pos.h);
 
@@ -311,7 +268,6 @@ bool CVideoPlayer::nextFrame()
 								pict.data[1], pict.linesize[1],
 								pict.data[2], pict.linesize[2]);
 						avpicture_free(&pict);
-#endif
 					}
 					else
 					{
@@ -387,22 +343,12 @@ void CVideoPlayer::close()
 		sws = nullptr;
 	}
 
-#ifdef VCMI_SDL1
-	if (overlay)
-	{
-		SDL_FreeYUVOverlay(overlay);
-		overlay = nullptr;
-	}
-#else
 	if (texture)
 	{
 		SDL_DestroyTexture(texture);
 		texture = nullptr;
 	}
 
-#endif
-
-
 	if (dest)
 	{
 		SDL_FreeSurface(dest);
@@ -411,8 +357,7 @@ void CVideoPlayer::close()
 
 	if (frame)
 	{
-		av_free(frame);
-		frame = nullptr;
+		av_frame_free(&frame);//will be set to null		
 	}
 
 	if (codec)
@@ -424,12 +369,7 @@ void CVideoPlayer::close()
 
 	if (format)
 	{
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 17, 0)
-		av_close_input_file(format);
-		format = nullptr;
-#else
 		avformat_close_input(&format);
-#endif
 	}
 
 	if (context)
@@ -455,13 +395,8 @@ bool CVideoPlayer::playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey)
 		if(stopOnKey && keyDown())
 			return false;
 
-#ifdef VCMI_SDL1
-		SDL_DisplayYUVOverlay(overlay, &pos);
-#else
 		SDL_RenderCopy(mainRenderer, texture, NULL, &pos);
 		SDL_RenderPresent(mainRenderer);
-#endif
-
 
 		// Wait 3 frames
 		GH.mainFPSmng->framerateDelay();

+ 28 - 20
client/CVideoHandler.h

@@ -47,26 +47,38 @@ public:
 
 #include <SDL.h>
 #include <SDL_video.h>
-#if SDL_VERSION_ATLEAST(1,3,0) && !SDL_VERSION_ATLEAST(2,0,0)
-#include <SDL_compat.h>
-#endif
 
 extern "C" {
 #include <libavformat/avformat.h>
 #include <libswscale/swscale.h>
+}
 
-// compatibility with different versions od libavutil
-#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 42, 0)) || \
-    (LIBAVUTIL_VERSION_INT == AV_VERSION_INT(51, 73, 101))
-
-#define AV_PIX_FMT_NONE         PIX_FMT_NONE
-#define AV_PIX_FMT_NV12         PIX_FMT_NV12
-#define AV_PIX_FMT_YUV420P      PIX_FMT_YUV420P
-#define AV_PIX_FMT_UYVY422      PIX_FMT_UYVY422
-#define AV_PIX_FMT_YUYV422      PIX_FMT_YUYV422
+//compatibility for libav 9.18 in ubuntu 14.04, 52.66.100 is ffmpeg 2.2.3
+#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 66, 100))
+inline AVFrame * av_frame_alloc()
+{
+	return avcodec_alloc_frame();
+}
 
-#endif 
+inline void av_frame_free(AVFrame ** frame)
+{
+	av_free(*frame);
+	*frame = nullptr;
 }
+#endif // VCMI_USE_OLD_AVUTIL
+
+//fix for travis-ci
+#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 0, 0))
+	#define AVPixelFormat PixelFormat
+	#define AV_PIX_FMT_NONE PIX_FMT_NONE
+	#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
+	#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
+	#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
+	#define AV_PIX_FMT_BGR32 PIX_FMT_BGR32
+	#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
+	#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
+	#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32
+#endif
 
 class CVideoPlayer : public IMainVideoPlayer
 {
@@ -74,18 +86,14 @@ class CVideoPlayer : public IMainVideoPlayer
 	AVFormatContext *format;
 	AVCodecContext *codecContext; // codec context for stream
 	AVCodec *codec;
-	AVFrame *frame; 
+	AVFrame *frame;
 	struct SwsContext *sws;
 
 	AVIOContext * context;
 
 	// Destination. Either overlay or dest.
-#ifdef VCMI_SDL1
-	SDL_Overlay * overlay;
-#else
-	SDL_Texture *texture;
-#endif
 
+	SDL_Texture *texture;
 	SDL_Surface *dest;
 	SDL_Rect destRect;			// valid when dest is used
 	SDL_Rect pos;				// destination on screen
@@ -109,7 +117,7 @@ public:
 	void show(int x, int y, SDL_Surface *dst, bool update = true) override; //blit current frame
 	void redraw(int x, int y, SDL_Surface *dst, bool update = true) override; //reblits buffer
 	void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
-	
+
 	// Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played)
 	bool openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey = false, bool scale = false) override;
 

+ 204 - 164
client/Client.cpp

@@ -19,7 +19,9 @@
 #include "../lib/CTownHandler.h"
 #include "../lib/CBuildingHandler.h"
 #include "../lib/spells/CSpellHandler.h"
-#include "../lib/Connection.h"
+#include "../lib/serializer/CTypeList.h"
+#include "../lib/serializer/Connection.h"
+#include "../lib/serializer/CLoadIntegrityValidator.h"
 #ifndef VCMI_ANDROID
 #include "../lib/Interprocess.h"
 #endif
@@ -30,7 +32,6 @@
 #include "../lib/JsonNode.h"
 #include "mapHandler.h"
 #include "../lib/CConfigHandler.h"
-#include "Client.h"
 #include "CPreGame.h"
 #include "battle/CBattleInterface.h"
 #include "../lib/CThreadHelper.h"
@@ -59,8 +60,8 @@ template <typename T> class CApplyOnCL;
 class CBaseForCLApply
 {
 public:
-	virtual void applyOnClAfter(CClient *cl, void *pack) const =0; 
-	virtual void applyOnClBefore(CClient *cl, void *pack) const =0; 
+	virtual void applyOnClAfter(CClient *cl, void *pack) const =0;
+	virtual void applyOnClBefore(CClient *cl, void *pack) const =0;
 	virtual ~CBaseForCLApply(){}
 
 	template<typename U> static CBaseForCLApply *getApplier(const U * t=nullptr)
@@ -72,12 +73,12 @@ public:
 template <typename T> class CApplyOnCL : public CBaseForCLApply
 {
 public:
-	void applyOnClAfter(CClient *cl, void *pack) const
+	void applyOnClAfter(CClient *cl, void *pack) const override
 	{
 		T *ptr = static_cast<T*>(pack);
 		ptr->applyCl(cl);
 	}
-	void applyOnClBefore(CClient *cl, void *pack) const
+	void applyOnClBefore(CClient *cl, void *pack) const override
 	{
 		T *ptr = static_cast<T*>(pack);
 		ptr->applyFirstCl(cl);
@@ -87,12 +88,12 @@ public:
 template <> class CApplyOnCL<CPack> : public CBaseForCLApply
 {
 public:
-	void applyOnClAfter(CClient *cl, void *pack) const
+	void applyOnClAfter(CClient *cl, void *pack) const override
 	{
 		logGlobal->errorStream() << "Cannot apply on CL plain CPack!";
 		assert(0);
 	}
-	void applyOnClBefore(CClient *cl, void *pack) const
+	void applyOnClBefore(CClient *cl, void *pack) const override
 	{
 		logGlobal->errorStream() << "Cannot apply on CL plain CPack!";
 		assert(0);
@@ -140,14 +141,17 @@ void CClient::waitForMoveAndSend(PlayerColor color)
 		setThreadName("CClient::waitForMoveAndSend");
 		assert(vstd::contains(battleints, color));
 		BattleAction ba = battleints[color]->activeStack(gs->curB->battleGetStackByID(gs->curB->activeStack, false));
-		logNetwork->traceStream() << "Send battle action to server: " << ba;
-		MakeAction temp_action(ba);
-		sendRequest(&temp_action, color);
+		if(ba.actionType != Battle::CANCEL)
+		{
+			logNetwork->traceStream() << "Send battle action to server: " << ba;
+			MakeAction temp_action(ba);
+			sendRequest(&temp_action, color);
+		}
 		return;
 	}
 	catch(boost::thread_interrupted&)
 	{
-        logNetwork->debugStream() << "Wait for move thread was interrupted and no action will be send. Was a battle ended by spell?";
+		logNetwork->debugStream() << "Wait for move thread was interrupted and no action will be send. Was a battle ended by spell?";
 		return;
 	}
 	catch(...)
@@ -155,7 +159,7 @@ void CClient::waitForMoveAndSend(PlayerColor color)
 		handleException();
 		return;
 	}
-    logNetwork->errorStream() << "We should not be here!";
+	logNetwork->errorStream() << "We should not be here!";
 }
 
 void CClient::run()
@@ -166,8 +170,8 @@ void CClient::run()
 		while(!terminate)
 		{
 			CPack *pack = serv->retreivePack(); //get the package from the server
-			
-			if (terminate) 
+
+			if (terminate)
 			{
 				vstd::clear_pointer(pack);
 				break;
@@ -175,15 +179,15 @@ void CClient::run()
 
 			handlePack(pack);
 		}
-	} 
+	}
 	//catch only asio exceptions
 	catch (const boost::system::system_error& e)
-	{	
-        logNetwork->errorStream() << "Lost connection to server, ending listening thread!";
-        logNetwork->errorStream() << e.what();
+	{
+		logNetwork->errorStream() << "Lost connection to server, ending listening thread!";
+		logNetwork->errorStream() << e.what();
 		if(!terminate) //rethrow (-> boom!) only if closing connection was unexpected
 		{
-            logNetwork->errorStream() << "Something wrong, lost connection while game is still ongoing...";
+			logNetwork->errorStream() << "Something wrong, lost connection while game is still ongoing...";
 			throw;
 		}
 	}
@@ -193,7 +197,7 @@ void CClient::save(const std::string & fname)
 {
 	if(gs->curB)
 	{
-        logNetwork->errorStream() << "Game cannot be saved during battle!";
+		logNetwork->errorStream() << "Game cannot be saved during battle!";
 		return;
 	}
 
@@ -201,33 +205,35 @@ void CClient::save(const std::string & fname)
 	sendRequest((CPackForClient*)&save_game, PlayerColor::NEUTRAL);
 }
 
-void CClient::endGame( bool closeConnection /*= true*/ )
+void CClient::endGame(bool closeConnection /*= true*/)
 {
 	//suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
-	for(auto i : playerint)
+	for (auto& i : playerint)
 		i.second->finish();
 
 	// Game is ending
 	// Tell the network thread to reach a stable state
-	if(closeConnection)
+	if (closeConnection)
 		stopConnection();
-    logNetwork->infoStream() << "Closed connection.";
+	logNetwork->infoStream() << "Closed connection.";
 
 	GH.curInt = nullptr;
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
-        logNetwork->infoStream() << "Ending current game!";
+		logNetwork->infoStream() << "Ending current game!";
 		if(GH.topInt())
+		{
 			GH.topInt()->deactivate();
+		}
 		GH.listInt.clear();
 		GH.objsToBlit.clear();
 		GH.statusbar = nullptr;
-        logNetwork->infoStream() << "Removed GUI.";
+		logNetwork->infoStream() << "Removed GUI.";
 
 		vstd::clear_pointer(const_cast<CGameInfo*>(CGI)->mh);
 		vstd::clear_pointer(gs);
 
-        logNetwork->infoStream() << "Deleted mapHandler and gameState.";
+		logNetwork->infoStream() << "Deleted mapHandler and gameState.";
 		LOCPLINT = nullptr;
 	}
 
@@ -235,30 +241,39 @@ void CClient::endGame( bool closeConnection /*= true*/ )
 	battleints.clear();
 	callbacks.clear();
 	battleCallbacks.clear();
-    logNetwork->infoStream() << "Deleted playerInts.";
-
-    logNetwork->infoStream() << "Client stopped.";
+	CGKeys::reset();
+	CGMagi::reset();
+	CGObelisk::reset();
+	logNetwork->infoStream() << "Deleted playerInts.";
+	logNetwork->infoStream() << "Client stopped.";
 }
 
 #if 1
 void CClient::loadGame(const std::string & fname, const bool server, const std::vector<int>& humanplayerindices, const int loadNumPlayers, int player_, const std::string & ipaddr, const std::string & port)
 {
-    PlayerColor player(player_); //intentional shadowing
-
-    logNetwork->infoStream() <<"Loading procedure started!";
+	PlayerColor player(player_); //intentional shadowing
+	logNetwork->infoStream() << "Loading procedure started!";
+
+	std::string realPort;
+	if(settings["testing"]["enabled"].Bool())
+		realPort = settings["testing"]["port"].String();
+	else if(port.size())
+		realPort = port;
+	else
+		realPort = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
 
 	CServerHandler sh;
-    if(server)
-         sh.startServer();
-    else
-         serv = sh.justConnectToServer(ipaddr,port=="" ? "3030" : port);
+	if(server)
+		sh.startServer();
+	else
+		serv = sh.justConnectToServer(ipaddr, realPort);
 
 	CStopWatch tmh;
-    unique_ptr<CLoadFile> loader;
+	std::unique_ptr<CLoadFile> loader;
 	try
 	{
-		std::string clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
-		std::string controlServerSaveName;
+		boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
+		boost::filesystem::path controlServerSaveName;
 
 		if (CResourceHandler::get("local")->existsResource(ResourceID(fname, EResType::SERVER_SAVEGAME)))
 		{
@@ -266,26 +281,26 @@ void CClient::loadGame(const std::string & fname, const bool server, const std::
 		}
 		else// create entry for server savegame. Triggered if save was made after launch and not yet present in res handler
 		{
-			controlServerSaveName = clientSaveName.substr(0, clientSaveName.find_last_of(".")) + ".vsgm1";
-			CResourceHandler::get("local")->createResource(controlServerSaveName, true);
+			controlServerSaveName = boost::filesystem::path(clientSaveName).replace_extension(".vsgm1");
+			CResourceHandler::get("local")->createResource(controlServerSaveName.string(), true);
 		}
 
 		if(clientSaveName.empty())
 			throw std::runtime_error("Cannot open client part of " + fname);
-		if(controlServerSaveName.empty())
+		if(controlServerSaveName.empty() || !boost::filesystem::exists(controlServerSaveName))
 			throw std::runtime_error("Cannot open server part of " + fname);
 
 		{
-			CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, minSupportedVersion);
+			CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, MINIMAL_SERIALIZATION_VERSION);
 			loadCommonState(checkingLoader);
 			loader = checkingLoader.decay();
 		}
-        logNetwork->infoStream() << "Loaded common part of save " << tmh.getDiff();
+		logNetwork->infoStream() << "Loaded common part of save " << tmh.getDiff();
 		const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
 		const_cast<CGameInfo*>(CGI)->mh->map = gs->map;
 		pathInfo = make_unique<CPathsInfo>(getMapSize());
 		CGI->mh->init();
-        logNetwork->infoStream() <<"Initing maphandler: "<<tmh.getDiff();
+		logNetwork->infoStream() <<"Initing maphandler: "<<tmh.getDiff();
 	}
 	catch(std::exception &e)
 	{
@@ -298,53 +313,53 @@ void CClient::loadGame(const std::string & fname, const bool server, const std::
          player = PlayerColor(player_);
 */
 
-    std::set<PlayerColor> clientPlayers;
-    if(server)
-         serv = sh.connectToServer();
+	std::set<PlayerColor> clientPlayers;
+	if(server)
+	serv = sh.connectToServer();
     //*loader >> *this;
 
-    if(server)
-    {
-         tmh.update();
-         ui8 pom8;
-         *serv << ui8(3) << ui8(loadNumPlayers); //load game; one client if single-player
-         *serv << fname;
-         *serv >> pom8;
-         if(pom8) 
-              throw std::runtime_error("Server cannot open the savegame!");
-         else
-              logNetwork->infoStream() << "Server opened savegame properly.";
-    }
-
-    if(server)
-    {
-         for(auto & elem : gs->scenarioOps->playerInfos)
-              if(!std::count(humanplayerindices.begin(),humanplayerindices.end(),elem.first.getNum()) || elem.first==player)
-              {
-                  clientPlayers.insert(elem.first);
-              }
-         clientPlayers.insert(PlayerColor::NEUTRAL);
-    }
-    else
-    {
-        clientPlayers.insert(player);
-    }
-
-    std::cout << "CLIENTPLAYERS:\n";
-    for(auto x : clientPlayers)
-         std::cout << x << std::endl;
-    std::cout << "ENDCLIENTPLAYERS\n";
-
-    serialize(loader->serializer,0,clientPlayers);
-    *serv << ui32(clientPlayers.size());
-    for(auto & elem : clientPlayers)
-        *serv << ui8(elem.getNum());
-    serv->addStdVecItems(gs); /*why is this here?*/
+	if(server)
+	{
+		tmh.update();
+		ui8 pom8;
+		*serv << ui8(3) << ui8(loadNumPlayers); //load game; one client if single-player
+		*serv << fname;
+		*serv >> pom8;
+		if(pom8)
+			throw std::runtime_error("Server cannot open the savegame!");
+		else
+			logNetwork->infoStream() << "Server opened savegame properly.";
+	}
+
+	if(server)
+	{
+		for(auto & elem : gs->scenarioOps->playerInfos)
+		{
+			if(!std::count(humanplayerindices.begin(),humanplayerindices.end(),elem.first.getNum()) || elem.first==player)
+				clientPlayers.insert(elem.first);
+		}
+		clientPlayers.insert(PlayerColor::NEUTRAL);
+	}
+	else
+	{
+		clientPlayers.insert(player);
+	}
+
+	std::cout << "CLIENTPLAYERS:\n";
+	for(auto x : clientPlayers)
+		std::cout << x << std::endl;
+	std::cout << "ENDCLIENTPLAYERS\n";
+
+	serialize(loader->serializer,0,clientPlayers);
+	*serv << ui32(clientPlayers.size());
+	for(auto & elem : clientPlayers)
+		*serv << ui8(elem.getNum());
+	serv->addStdVecItems(gs); /*why is this here?*/
 
     //*loader >> *this;
-    logNetwork->infoStream() << "Loaded client part of save " << tmh.getDiff();
+	logNetwork->infoStream() << "Loaded client part of save " << tmh.getDiff();
 
-    logNetwork->infoStream() <<"Sent info to server: "<<tmh.getDiff();
+	logNetwork->infoStream() <<"Sent info to server: "<<tmh.getDiff();
 
     //*serv << clientPlayers;
 	serv->enableStackSendingByID();
@@ -366,7 +381,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 {
 	enum {SINGLE, HOST, GUEST} networkMode = SINGLE;
 
-	if (con == nullptr) 
+	if (con == nullptr)
 	{
 		CServerHandler sh;
 		serv = sh.connectToServer();
@@ -393,7 +408,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	}
 
 	c >> si;
-    logNetwork->infoStream() <<"\tSending/Getting info to/from the server: "<<tmh.getDiff();
+	logNetwork->infoStream() <<"\tSending/Getting info to/from the server: "<<tmh.getDiff();
 	c.enableStackSendingByID();
 	c.disableSmartPointerSerialization();
 
@@ -403,7 +418,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 
 	gs->scenarioOps = si;
 	gs->init(si);
-    logNetwork->infoStream() <<"Initializing GameState (together): "<<tmh.getDiff();
+	logNetwork->infoStream() <<"Initializing GameState (together): "<<tmh.getDiff();
 
 	// Now after possible random map gen, we know exact player count.
 	// Inform server about how many players client handles
@@ -426,10 +441,10 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	{
 		const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
 		CGI->mh->map = gs->map;
-        logNetwork->infoStream() <<"Creating mapHandler: "<<tmh.getDiff();
+		logNetwork->infoStream() << "Creating mapHandler: " << tmh.getDiff();
 		CGI->mh->init();
 		pathInfo = make_unique<CPathsInfo>(getMapSize());
-        logNetwork->infoStream() <<"Initializing mapHandler (together): "<<tmh.getDiff();
+		logNetwork->infoStream() << "Initializing mapHandler (together): " << tmh.getDiff();
 	}
 
 	int humanPlayers = 0;
@@ -440,7 +455,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 		if(!vstd::contains(myPlayers, color))
 			continue;
 
-        logNetwork->traceStream() << "Preparing interface for player " << color;
+		logNetwork->traceStream() << "Preparing interface for player " << color;
 		if(si->mode != StartInfo::DUEL)
 		{
 			if(elem.second.playerID == PlayerSettings::PLAYER_AI)
@@ -449,9 +464,9 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 				logNetwork->infoStream() << boost::format("Player %s will be lead by %s") % color % AiToGive;
 				installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color);
 			}
-			else 
+			else
 			{
-				installNewPlayerInterface(make_shared<CPlayerInterface>(color), color);
+				installNewPlayerInterface(std::make_shared<CPlayerInterface>(color), color);
 				humanPlayers++;
 			}
 		}
@@ -467,7 +482,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 		if(!gNoGUI)
 		{
 			boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
-			auto p = make_shared<CPlayerInterface>(PlayerColor::NEUTRAL);
+			auto p = std::make_shared<CPlayerInterface>(PlayerColor::NEUTRAL);
 			p->observerInDuelMode = true;
 			installNewPlayerInterface(p, boost::none);
 			GH.curInt = p.get();
@@ -492,15 +507,15 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 // 		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
 // 	}
 }
 
-void CClient::serialize(COSer & h, const int version)
+void CClient::serialize(BinarySerializer & h, const int version)
 {
 	assert(h.saving);
-	h & hotSeat;	
+	h & hotSeat;
 	{
 		ui8 players = playerint.size();
 		h & players;
@@ -510,12 +525,12 @@ void CClient::serialize(COSer & h, const int version)
 			LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
 			assert(i->first == i->second->playerID);
 			h & i->first & i->second->dllName & i->second->human;
-			i->second->saveGame(h, version); 			
+			i->second->saveGame(h, version);
 		}
 	}
 }
 
-void CClient::serialize(CISer & h, const int version)
+void CClient::serialize(BinaryDeserializer & h, const int version)
 {
 	assert(!h.saving);
 	h & hotSeat;
@@ -526,19 +541,19 @@ void CClient::serialize(CISer & h, const int version)
 		for(int i=0; i < players; i++)
 		{
 			std::string dllname;
-			PlayerColor pid; 
+			PlayerColor pid;
 			bool isHuman = false;
 
 			h & pid & dllname & isHuman;
 			LOG_TRACE_PARAMS(logGlobal, "Loading player %s interface", pid);
 
-			shared_ptr<CGameInterface> nInt;
+			std::shared_ptr<CGameInterface> nInt;
 			if(dllname.length())
 			{
 				if(pid == PlayerColor::NEUTRAL)
 				{
 					installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
-					//TODO? consider serialization 
+					//TODO? consider serialization
 					continue;
 				}
 				else
@@ -550,7 +565,7 @@ void CClient::serialize(CISer & h, const int version)
 			else
 			{
 				assert(isHuman);
-				nInt = make_shared<CPlayerInterface>(pid);
+				nInt = std::make_shared<CPlayerInterface>(pid);
 			}
 
 			nInt->dllName = dllname;
@@ -566,7 +581,7 @@ void CClient::serialize(CISer & h, const int version)
 	}
 }
 
-void CClient::serialize(COSer & h, const int version, const std::set<PlayerColor> & playerIDs)
+void CClient::serialize(BinarySerializer & h, const int version, const std::set<PlayerColor> & playerIDs)
 {
 	assert(h.saving);
 	h & hotSeat;
@@ -579,12 +594,12 @@ void CClient::serialize(COSer & h, const int version, const std::set<PlayerColor
 			LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
 			assert(i->first == i->second->playerID);
 			h & i->first & i->second->dllName & i->second->human;
-			i->second->saveGame(h, version); 			
+			i->second->saveGame(h, version);
 		}
 	}
 }
 
-void CClient::serialize(CISer & h, const int version, const std::set<PlayerColor> & playerIDs)
+void CClient::serialize(BinaryDeserializer & h, const int version, const std::set<PlayerColor> & playerIDs)
 {
 	assert(!h.saving);
 	h & hotSeat;
@@ -595,20 +610,20 @@ void CClient::serialize(CISer & h, const int version, const std::set<PlayerColor
 		for(int i=0; i < players; i++)
 		{
 			std::string dllname;
-			PlayerColor pid; 
+			PlayerColor pid;
 			bool isHuman = false;
 
 			h & pid & dllname & isHuman;
 			LOG_TRACE_PARAMS(logGlobal, "Loading player %s interface", pid);
 
-			shared_ptr<CGameInterface> nInt;
+			std::shared_ptr<CGameInterface> nInt;
 			if(dllname.length())
 			{
 				if(pid == PlayerColor::NEUTRAL)
 				{
-                    if(playerIDs.count(pid))
-                       installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
-					//TODO? consider serialization 
+					if(playerIDs.count(pid))
+						installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
+					//TODO? consider serialization
 					continue;
 				}
 				else
@@ -620,49 +635,55 @@ void CClient::serialize(CISer & h, const int version, const std::set<PlayerColor
 			else
 			{
 				assert(isHuman);
-				nInt = make_shared<CPlayerInterface>(pid);
+				nInt = std::make_shared<CPlayerInterface>(pid);
 			}
 
 			nInt->dllName = dllname;
 			nInt->human = isHuman;
 			nInt->playerID = pid;
 
-            if(playerIDs.count(pid))
-                 installNewPlayerInterface(nInt, pid);
+			if(playerIDs.count(pid))
+				installNewPlayerInterface(nInt, pid);
 
-            nInt->loadGame(h, version);       
+			nInt->loadGame(h, version);
 		}
 
 		if(playerIDs.count(PlayerColor::NEUTRAL))
-            loadNeutralBattleAI();
+			loadNeutralBattleAI();
 	}
 }
 
 void CClient::handlePack( CPack * pack )
 {
-	CBaseForCLApply *apply = applier->apps[typeList.getTypeID(pack)]; //find the applier
+	if(pack == nullptr)
+	{
+		logNetwork->error("Dropping nullptr CPack! You should check whether client and server ABI matches.");
+		return;
+	}
+	CBaseForCLApply *apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier
 	if(apply)
 	{
 		boost::unique_lock<boost::recursive_mutex> guiLock(*LOCPLINT->pim);
-		apply->applyOnClBefore(this,pack);
-        logNetwork->traceStream() << "\tMade first apply on cl";
+		apply->applyOnClBefore(this, pack);
+		logNetwork->trace("\tMade first apply on cl");
 		gs->apply(pack);
-        logNetwork->traceStream() << "\tApplied on gs";
-		apply->applyOnClAfter(this,pack);
-        logNetwork->traceStream() << "\tMade second apply on cl";
+		logNetwork->trace("\tApplied on gs");
+		apply->applyOnClAfter(this, pack);
+		logNetwork->trace("\tMade second apply on cl");
 	}
 	else
 	{
-        logNetwork->errorStream() << "Message cannot be applied, cannot find applier! TypeID " << typeList.getTypeID(pack);
+		logNetwork->error("Message cannot be applied, cannot find applier! type %d - %s",
+                      pack->type, typeList.getTypeInfo(pack)->name());
 	}
 	delete pack;
 }
 
-void CClient::finishCampaign( shared_ptr<CCampaignState> camp )
+void CClient::finishCampaign( std::shared_ptr<CCampaignState> camp )
 {
 }
 
-void CClient::proposeNextMission(shared_ptr<CCampaignState> camp)
+void CClient::proposeNextMission(std::shared_ptr<CCampaignState> camp)
 {
 	GH.pushInt(new CBonusSelection(camp));
 }
@@ -673,11 +694,11 @@ void CClient::stopConnection()
 
 	if (serv) //request closing connection
 	{
-        logNetwork->infoStream() << "Connection has been requested to be closed.";
+		logNetwork->infoStream() << "Connection has been requested to be closed.";
 		boost::unique_lock<boost::mutex>(*serv->wmx);
 		CloseServer close_server;
 		sendRequest(&close_server, PlayerColor::NEUTRAL);
-        logNetwork->infoStream() << "Sent closing signal to the server";
+		logNetwork->infoStream() << "Sent closing signal to the server";
 	}
 
 	if(connectionHandler)//end connection handler
@@ -685,7 +706,7 @@ void CClient::stopConnection()
 		if(connectionHandler->get_id() != boost::this_thread::get_id())
 			connectionHandler->join();
 
-        logNetwork->infoStream() << "Connection handler thread joined";
+		logNetwork->infoStream() << "Connection handler thread joined";
 
 		delete connectionHandler;
 		connectionHandler = nullptr;
@@ -696,7 +717,7 @@ void CClient::stopConnection()
 		serv->close();
 		delete serv;
 		serv = nullptr;
-        logNetwork->warnStream() << "Our socket has been closed.";
+		logNetwork->warnStream() << "Our socket has been closed.";
 	}
 }
 
@@ -704,7 +725,7 @@ void CClient::battleStarted(const BattleInfo * info)
 {
 	for(auto &battleCb : battleCallbacks)
 	{
-		if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })  
+		if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })
 			||  battleCb.first >= PlayerColor::PLAYER_LIMIT)
 		{
 			battleCb.second->setBattle(info);
@@ -714,7 +735,7 @@ void CClient::battleStarted(const BattleInfo * info)
 // 		if(battleCallbacks.count(side))
 // 			battleCallbacks[side]->setBattle(info);
 
-	shared_ptr<CPlayerInterface> att, def;
+	std::shared_ptr<CPlayerInterface> att, def;
 	auto &leftSide = info->sides[0], &rightSide = info->sides[1];
 
 
@@ -732,7 +753,7 @@ void CClient::battleStarted(const BattleInfo * info)
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
 		auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
-			Rect((screen->w - 800)/2, 
+			Rect((screen->w - 800)/2,
 			     (screen->h - 600)/2, 800, 600), att, def);
 
 		GH.pushInt(bi);
@@ -780,7 +801,7 @@ PlayerColor CClient::getLocalPlayer() const
 	return getCurrentPlayer();
 }
 
-void CClient::commenceTacticPhaseForInt(shared_ptr<CBattleGameInterface> battleInt)
+void CClient::commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> battleInt)
 {
 	setThreadName("CClient::commenceTacticPhaseForInt");
 	try
@@ -795,7 +816,7 @@ void CClient::commenceTacticPhaseForInt(shared_ptr<CBattleGameInterface> battleI
 	catch(...)
 	{
 		handleException();
-	}	
+	}
 }
 
 void CClient::invalidatePaths()
@@ -821,7 +842,7 @@ int CClient::sendRequest(const CPack *request, PlayerColor player)
 	static ui32 requestCounter = 0;
 
 	ui32 requestID = requestCounter++;
-    logNetwork->traceStream() << boost::format("Sending a request \"%s\". It'll have an ID=%d.")
+	logNetwork->traceStream() << boost::format("Sending a request \"%s\". It'll have an ID=%d.")
 				% typeid(*request).name() % requestID;
 
 	waitingRequest.pushBack(requestID);
@@ -832,7 +853,7 @@ int CClient::sendRequest(const CPack *request, PlayerColor player)
 	return requestID;
 }
 
-void CClient::campaignMapFinished( shared_ptr<CCampaignState> camp )
+void CClient::campaignMapFinished( std::shared_ptr<CCampaignState> camp )
 {
 	endGame(false);
 
@@ -855,7 +876,7 @@ void CClient::campaignMapFinished( shared_ptr<CCampaignState> camp )
 	}
 }
 
-void CClient::installNewPlayerInterface(shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color)
+void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
 	PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
@@ -866,7 +887,7 @@ void CClient::installNewPlayerInterface(shared_ptr<CGameInterface> gameInterface
 	playerint[colorUsed] = gameInterface;
 
 	logGlobal->traceStream() << boost::format("\tInitializing the interface for player %s") % colorUsed;
-	auto cb = make_shared<CCallback>(gs, color, this);
+	auto cb = std::make_shared<CCallback>(gs, color, this);
 	callbacks[colorUsed] = cb;
 	battleCallbacks[colorUsed] = cb;
 	gameInterface->init(cb);
@@ -874,12 +895,12 @@ void CClient::installNewPlayerInterface(shared_ptr<CGameInterface> gameInterface
 	installNewBattleInterface(gameInterface, color, false);
 }
 
-void CClient::installNewBattleInterface(shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback /*= true*/)
+void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback /*= true*/)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
 	PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
 
-	if(!color) 
+	if(!color)
 		privilagedBattleEventReceivers.push_back(battleInterface);
 
 	battleints[colorUsed] = battleInterface;
@@ -887,7 +908,7 @@ void CClient::installNewBattleInterface(shared_ptr<CBattleGameInterface> battleI
 	if(needCallback)
 	{
 		logGlobal->traceStream() << boost::format("\tInitializing the battle interface for player %s") % *color;
-		auto cbc = make_shared<CBattleCallback>(gs, color, this);
+		auto cbc = std::make_shared<CBattleCallback>(gs, color, this);
 		battleCallbacks[colorUsed] = cbc;
 		battleInterface->init(cbc);
 	}
@@ -915,17 +936,25 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
 	return goodAI;
 }
 
+bool CServerHandler::DO_NOT_START_SERVER = false;
 
 void CServerHandler::startServer()
 {
+	if(DO_NOT_START_SERVER)
+		return;
+
 	th.update();
+
 	serverThread = new boost::thread(&CServerHandler::callServer, this); //runs server executable;
 	if(verbose)
-        logNetwork->infoStream() << "Setting up thread calling server: " << th.getDiff();
+		logNetwork->infoStream() << "Setting up thread calling server: " << th.getDiff();
 }
 
 void CServerHandler::waitForServer()
 {
+	if(DO_NOT_START_SERVER)
+		return;
+
 	if(!serverThread)
 		startServer();
 
@@ -938,7 +967,7 @@ void CServerHandler::waitForServer()
 	}
 #endif
 	if(verbose)
-        logNetwork->infoStream() << "Waiting for server: " << th.getDiff();
+		logNetwork->infoStream() << "Waiting for server: " << th.getDiff();
 }
 
 CConnection * CServerHandler::connectToServer()
@@ -951,11 +980,11 @@ CConnection * CServerHandler::connectToServer()
 #endif
 
 	th.update(); //put breakpoint here to attach to server before it does something stupid
-    
+
 	CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port);
 
 	if(verbose)
-        logNetwork->infoStream()<<"\tConnecting to the server: "<<th.getDiff();
+		logNetwork->infoStream()<<"\tConnecting to the server: "<<th.getDiff();
 
 	return ret;
 }
@@ -964,7 +993,10 @@ CServerHandler::CServerHandler(bool runServer /*= false*/)
 {
 	serverThread = nullptr;
 	shared = nullptr;
-	port = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
+	if(settings["testing"]["enabled"].Bool())
+		port = settings["testing"]["port"].String();
+	else
+		port = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
 	verbose = true;
 
 #ifndef VCMI_ANDROID
@@ -972,13 +1004,13 @@ CServerHandler::CServerHandler(bool runServer /*= false*/)
 	try
 	{
 		shared = new SharedMem();
-    }
-    catch(...)
-    {
-    	logNetwork->error("Cannot open interprocess memory.");
-    	handleException();
-    	throw;
-    }
+	}
+	catch(...)
+	{
+		logNetwork->error("Cannot open interprocess memory.");
+		handleException();
+		throw;
+	}
 #endif
 }
 
@@ -995,30 +1027,38 @@ void CServerHandler::callServer()
 	const std::string comm = VCMIDirs::get().serverPath().string() + " --port=" + port + " > \"" + logName + '\"';
 	int result = std::system(comm.c_str());
 	if (result == 0)
-        logNetwork->infoStream() << "Server closed correctly";
+		logNetwork->infoStream() << "Server closed correctly";
 	else
 	{
-        logNetwork->errorStream() << "Error: server failed to close correctly or crashed!";
-        logNetwork->errorStream() << "Check " << logName << " for more info";
+		logNetwork->errorStream() << "Error: server failed to close correctly or crashed!";
+		logNetwork->errorStream() << "Check " << logName << " for more info";
 		exit(1);// exit in case of error. Othervice without working server VCMI will hang
 	}
 }
 
 CConnection * CServerHandler::justConnectToServer(const std::string &host, const std::string &port)
 {
+	std::string realPort;
+	if(settings["testing"]["enabled"].Bool())
+		realPort = settings["testing"]["port"].String();
+	else if(port.size())
+		realPort = port;
+	else
+		realPort = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
+
 	CConnection *ret = nullptr;
 	while(!ret)
 	{
 		try
 		{
-            logNetwork->infoStream() << "Establishing connection...";
-			ret = new CConnection(	host.size() ? host : settings["server"]["server"].String(), 
-									port.size() ? port : boost::lexical_cast<std::string>(settings["server"]["port"].Float()), 
+			logNetwork->infoStream() << "Establishing connection...";
+			ret = new CConnection(	host.size() ? host : settings["server"]["server"].String(),
+									realPort,
 									NAME);
 		}
 		catch(...)
 		{
-            logNetwork->errorStream() << "\nCannot establish connection! Retrying within 2 seconds";
+			logNetwork->errorStream() << "\nCannot establish connection! Retrying within 2 seconds";
 			SDL_Delay(2000);
 		}
 	}

+ 32 - 33
client/Client.h

@@ -16,7 +16,7 @@
  *
  */
 
-class CPack;
+struct CPack;
 class CCampaignState;
 class CBattleCallback;
 class IGameEventsReceiver;
@@ -32,8 +32,8 @@ struct SharedMem;
 class CClient;
 class CScriptingModule;
 struct CPathsInfo;
-class CISer;
-class COSer;
+class BinaryDeserializer;
+class BinarySerializer;
 namespace boost { class thread; }
 
 /// structure to handle running server and connecting to it
@@ -42,6 +42,8 @@ class CServerHandler
 private:
 	void callServer(); //calls server via system(), should be called as thread
 public:
+	static bool DO_NOT_START_SERVER;
+
 	CStopWatch th;
 	boost::thread *serverThread; //thread that called system to run server
 	SharedMem *shared; //interprocess memory (for waiting for server)
@@ -57,7 +59,7 @@ public:
 	static CConnection * justConnectToServer(const std::string &host = "", const std::string &port = ""); //connects to given host without taking any other actions (like setting up server)
 
 	CServerHandler(bool runServer = false);
-	~CServerHandler();
+	virtual ~CServerHandler();
 };
 
 template<typename T>
@@ -70,7 +72,6 @@ class ThreadSafeVector
 	boost::condition_variable cond;
 
 public:
-
 	void pushBack(const T &item)
 	{
 		TLock lock(mx);
@@ -115,17 +116,17 @@ public:
 /// Class which handles client - server logic
 class CClient : public IGameCallback
 {
-	unique_ptr<CPathsInfo> pathInfo;
+	std::unique_ptr<CPathsInfo> pathInfo;
 public:
-	std::map<PlayerColor,shared_ptr<CCallback> > callbacks; //callbacks given to player interfaces
-	std::map<PlayerColor,shared_ptr<CBattleCallback> > battleCallbacks; //callbacks given to player interfaces
-	std::vector<shared_ptr<IGameEventsReceiver>> privilagedGameEventReceivers; //scripting modules, spectator interfaces
-	std::vector<shared_ptr<IBattleEventsReceiver>> privilagedBattleEventReceivers; //scripting modules, spectator interfaces
-	std::map<PlayerColor, shared_ptr<CGameInterface>> playerint;
-	std::map<PlayerColor, shared_ptr<CBattleGameInterface>> battleints;
+	std::map<PlayerColor,std::shared_ptr<CCallback> > callbacks; //callbacks given to player interfaces
+	std::map<PlayerColor,std::shared_ptr<CBattleCallback> > battleCallbacks; //callbacks given to player interfaces
+	std::vector<std::shared_ptr<IGameEventsReceiver>> privilagedGameEventReceivers; //scripting modules, spectator interfaces
+	std::vector<std::shared_ptr<IBattleEventsReceiver>> privilagedBattleEventReceivers; //scripting modules, spectator interfaces
+	std::map<PlayerColor, std::shared_ptr<CGameInterface>> playerint;
+	std::map<PlayerColor, std::shared_ptr<CBattleGameInterface>> battleints;
 
-	std::map<PlayerColor,std::vector<shared_ptr<IGameEventsReceiver>>> additionalPlayerInts;
-	std::map<PlayerColor,std::vector<shared_ptr<IBattleEventsReceiver>>> additionalBattleInts;
+	std::map<PlayerColor,std::vector<std::shared_ptr<IGameEventsReceiver>>> additionalPlayerInts;
+	std::map<PlayerColor,std::vector<std::shared_ptr<IBattleEventsReceiver>>> additionalBattleInts;
 
 	bool hotSeat;
 	CConnection *serv;
@@ -146,8 +147,8 @@ public:
 	void newGame(CConnection *con, StartInfo *si); //con - connection to server
 
 	void loadNeutralBattleAI();
-	void installNewPlayerInterface(shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color);
-	void installNewBattleInterface(shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback = true);
+	void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color);
+	void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback = true);
 	std::string aiNameForPlayer(const PlayerSettings &ps, bool battleAI); //empty means no AI -> human
 
 	void endGame(bool closeConnection = true);
@@ -155,9 +156,9 @@ public:
 	void save(const std::string & fname);
 	void loadGame(const std::string & fname, const bool server = true, const std::vector<int>& humanplayerindices = std::vector<int>(), const int loadnumplayers = 1, int player_ = -1, const std::string & ipaddr = "", const std::string & port = "");
 	void run();
-	void campaignMapFinished( shared_ptr<CCampaignState> camp );
-	void finishCampaign( shared_ptr<CCampaignState> camp );
-	void proposeNextMission(shared_ptr<CCampaignState> camp);
+	void campaignMapFinished( std::shared_ptr<CCampaignState> camp );
+	void finishCampaign( std::shared_ptr<CCampaignState> camp );
+	void proposeNextMission(std::shared_ptr<CCampaignState> camp);
 
 	void invalidatePaths();
 	const CPathsInfo * getPathsInfo(const CGHeroInstance *h);
@@ -175,9 +176,9 @@ public:
 	void setBlockVis(ObjectInstanceID objid, bool bv) override {};
 	void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) override {};
-	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override {}; 
+	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override {};
 
-	void showBlockingDialog(BlockingDialog *iw) override {}; 
+	void showBlockingDialog(BlockingDialog *iw) override {};
 	void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {};
 	void showTeleportDialog(TeleportDialog *iw) override {};
 	void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override {};
@@ -186,20 +187,20 @@ public:
 
 	void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override {};
 	void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override {};
-	bool changeStackType(const StackLocation &sl, CCreature *c) override {return false;};
+	bool changeStackType(const StackLocation &sl, const CCreature *c) override {return false;};
 	bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override {return false;};
 	bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) override {return false;};
-	bool eraseStack(const StackLocation &sl, bool forceRemoval = false){return false;};
+	bool eraseStack(const StackLocation &sl, bool forceRemoval = false) override{return false;};
 	bool swapStacks(const StackLocation &sl1, const StackLocation &sl2) override {return false;}
 	bool addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count) override {return false;}
 	void tryJoiningArmy(const CArmedInstance *src, const CArmedInstance *dst, bool removeObjWhenFinished, bool allowMerging) override {}
 	bool moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count = -1) override {return false;}
-	
+
 	void removeAfterVisit(const CGObjectInstance *object) override {};
 
 	void giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, ArtifactPosition pos) override {};
 	void giveHeroArtifact(const CGHeroInstance *h, const CArtifactInstance *a, ArtifactPosition pos) override {};
-	void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) override {}; 
+	void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) override {};
 	void removeArtifact(const ArtifactLocation &al) override {};
 	bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) override {return false;};
 	void synchronizeArtifactHandlerLists() override {};
@@ -207,8 +208,6 @@ public:
 	void showCompInfo(ShowInInfobox * comp) override {};
 	void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
 	void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
-	//void giveHeroArtifact(int artid, int hid, int position){}; 
-	//void giveNewArtifact(int hid, int position){};
 	void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = nullptr) override {}; //use hero=nullptr for no hero
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
@@ -233,16 +232,16 @@ public:
 
 	void handlePack( CPack * pack ); //applies the given pack and deletes it
 	void battleStarted(const BattleInfo * info);
-	void commenceTacticPhaseForInt(shared_ptr<CBattleGameInterface> battleInt); //will be called as separate thread
+	void commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> battleInt); //will be called as separate thread
 
 	void commitPackage(CPackForClient *pack) override;
 
 	//////////////////////////////////////////////////////////////////////////
 
-	void serialize(COSer &h, const int version);
-	void serialize(CISer &h, const int version);
-	
-	void serialize(COSer &h, const int version, const std::set<PlayerColor>& playerIDs);
-	void serialize(CISer &h, const int version, const std::set<PlayerColor>& playerIDs);
+	void serialize(BinarySerializer & h, const int version);
+	void serialize(BinaryDeserializer & h, const int version);
+
+	void serialize(BinarySerializer & h, const int version, const std::set<PlayerColor>& playerIDs);
+	void serialize(BinaryDeserializer & h, const int version, const std::set<PlayerColor>& playerIDs);
 	void battleFinished();
 };

+ 55 - 33
client/Graphics.cpp

@@ -5,6 +5,7 @@
 #include "../lib/filesystem/CBinaryReader.h"
 #include "CDefHandler.h"
 #include "gui/SDL_Extensions.h"
+#include "gui/CAnimation.h"
 #include <SDL_ttf.h>
 #include "../lib/CThreadHelper.h"
 #include "CGameInfo.h"
@@ -19,9 +20,9 @@
 #include "../lib/CGameState.h"
 #include "../lib/JsonNode.h"
 #include "../lib/vcmi_endian.h"
-#include "../lib/GameConstants.h"
 #include "../lib/CStopWatch.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h"
+#include "../lib/mapObjects/CObjectHandler.h"
 
 using namespace CSDL_Ext;
 #ifdef min
@@ -58,7 +59,7 @@ void Graphics::loadPaletteAndColors()
 		col.r = pals[startPoint++];
 		col.g = pals[startPoint++];
 		col.b = pals[startPoint++];
-		CSDL_Ext::colorSetAlpha(col,SDL_ALPHA_OPAQUE);
+		col.a = SDL_ALPHA_OPAQUE;
 		startPoint++;
 		playerColorPalette[i] = col;
 	}
@@ -74,32 +75,35 @@ void Graphics::loadPaletteAndColors()
 		neutralColorPalette[i].g = reader.readUInt8();
 		neutralColorPalette[i].b = reader.readUInt8();
 		reader.readUInt8(); // this is "flags" entry, not alpha
-		CSDL_Ext::colorSetAlpha(neutralColorPalette[i], SDL_ALPHA_OPAQUE);
+		neutralColorPalette[i].a = SDL_ALPHA_OPAQUE;
 	}
 	//colors initialization
-	SDL_Color colors[]  = { 
-		{0xff,0,  0,    SDL_ALPHA_OPAQUE}, 
+	SDL_Color colors[]  = {
+		{0xff,0,  0,    SDL_ALPHA_OPAQUE},
 		{0x31,0x52,0xff,SDL_ALPHA_OPAQUE},
 		{0x9c,0x73,0x52,SDL_ALPHA_OPAQUE},
 		{0x42,0x94,0x29,SDL_ALPHA_OPAQUE},
-		
+
 		{0xff,0x84,0,   SDL_ALPHA_OPAQUE},
 		{0x8c,0x29,0xa5,SDL_ALPHA_OPAQUE},
 		{0x09,0x9c,0xa5,SDL_ALPHA_OPAQUE},
-		{0xc6,0x7b,0x8c,SDL_ALPHA_OPAQUE}};		
-		
+		{0xc6,0x7b,0x8c,SDL_ALPHA_OPAQUE}};
+
 	for(int i=0;i<8;i++)
 	{
 		playerColors[i] = colors[i];
 	}
-	neutralColor->r = 0x84; neutralColor->g = 0x84; neutralColor->b = 0x84; //gray
-	CSDL_Ext::colorSetAlpha(*neutralColor,SDL_ALPHA_OPAQUE);
+	//gray
+	neutralColor->r = 0x84;
+	neutralColor->g = 0x84;
+	neutralColor->b = 0x84;
+	neutralColor->a = SDL_ALPHA_OPAQUE;
 }
 
 void Graphics::initializeBattleGraphics()
 {
 	const JsonNode config(ResourceID("config/battles_graphics.json"));
-	
+
 	// Reserve enough space for the terrains
 	int idx = config["backgrounds"].Vector().size();
 	battleBacks.resize(idx+1);	// 1 to idx, 0 is unused
@@ -120,11 +124,20 @@ void Graphics::initializeBattleGraphics()
 		}
 
 		battleACToDef[ACid] = toAdd;
-	}	
+	}
 }
 Graphics::Graphics()
 {
 	#if 0
+
+	#define GET_DATA(TYPE,DESTINATION,FUNCTION_TO_GET) \
+		(std::bind(&setData<TYPE>,&DESTINATION,FUNCTION_TO_GET))
+
+	#define GET_DEF_ESS(DESTINATION, DEF_NAME) \
+		(GET_DATA \
+			(CDefEssential*,DESTINATION,\
+			std::function<CDefEssential*()>(std::bind(CDefHandler::giveDefEss,DEF_NAME))))
+
 	std::vector<Task> tasks; //preparing list of graphics to load
 	tasks += std::bind(&Graphics::loadFonts,this);
 	tasks += std::bind(&Graphics::loadPaletteAndColors,this);
@@ -132,8 +145,6 @@ Graphics::Graphics()
 	tasks += std::bind(&Graphics::initializeBattleGraphics,this);
 	tasks += std::bind(&Graphics::loadErmuToPicture,this);
 	tasks += std::bind(&Graphics::initializeImageLists,this);
-	tasks += GET_DEF_ESS(resources32,"RESOURCE.DEF");	
-	tasks += GET_DEF_ESS(heroMoveArrows,"ADAG.DEF");
 
 	CThreadHelper th(&tasks,std::max((ui32)1,boost::thread::hardware_concurrency()));
 	th.run();
@@ -144,20 +155,23 @@ Graphics::Graphics()
 	initializeBattleGraphics();
 	loadErmuToPicture();
 	initializeImageLists();
-	resources32 = CDefHandler::giveDefEss("RESOURCE.DEF");
-	heroMoveArrows = CDefHandler::giveDefEss("ADAG.DEF");
 	#endif
 
-	for(auto & elem : heroMoveArrows->ourImages)
-	{
-		CSDL_Ext::alphaTransform(elem.bitmap);
-	}
+	//(!) do not load any CAnimation here
+}
+
+void Graphics::load()
+{
+	heroMoveArrows = std::make_shared<CAnimation>("ADAG");
+	heroMoveArrows->preload();
+
+	loadHeroAnims();
 }
 
 void Graphics::loadHeroAnims()
 {
 	//first - group number to be rotated1, second - group number after rotation1
-	std::vector<std::pair<int,int> > rotations = 
+	std::vector<std::pair<int,int> > rotations =
 	{
 		{6,10}, {7,11}, {8,12}, {1,13},
 		{2,14}, {3,15}
@@ -219,11 +233,11 @@ void Graphics::loadHeroFlagsDetail(std::pair<std::vector<CDefEssential *> Graphi
 	for(int i=0;i<8;i++)
 		(this->*pr.first).push_back(CDefHandler::giveDefEss(pr.second[i]));
 	//first - group number to be rotated1, second - group number after rotation1
-	std::vector<std::pair<int,int> > rotations = 
+	std::vector<std::pair<int,int> > rotations =
 	{
 		{6,10}, {7,11}, {8,12}
 	};
-	
+
 	for(int q=0; q<8; ++q)
 	{
 		std::vector<Cimage> &curImgs = (this->*pr.first)[q]->ourImages;
@@ -266,9 +280,7 @@ void Graphics::loadHeroFlagsDetail(std::pair<std::vector<CDefEssential *> Graphi
 		for(auto & curImg : curImgs)
 		{
 			CSDL_Ext::setDefaultColorKey(curImg.bitmap);
-			#ifndef VCMI_SDL1
 			SDL_SetSurfaceBlendMode(curImg.bitmap,SDL_BLENDMODE_NONE);
-			#endif
 		}
 	}
 }
@@ -279,15 +291,15 @@ void Graphics::loadHeroFlags()
 	std::pair<std::vector<CDefEssential *> Graphics::*, std::vector<const char *> > pr[4] =
 	{
 		{
-			&Graphics::flags1, 
+			&Graphics::flags1,
 			{"ABF01L.DEF","ABF01G.DEF","ABF01R.DEF","ABF01D.DEF","ABF01B.DEF",
-			"ABF01P.DEF","ABF01W.DEF","ABF01K.DEF"} 
+			"ABF01P.DEF","ABF01W.DEF","ABF01K.DEF"}
 		},
 		{
 			&Graphics::flags2,
 			{"ABF02L.DEF","ABF02G.DEF","ABF02R.DEF","ABF02D.DEF","ABF02B.DEF",
 			"ABF02P.DEF","ABF02W.DEF","ABF02K.DEF"}
-			
+
 		},
 		{
 			&Graphics::flags3,
@@ -298,7 +310,7 @@ void Graphics::loadHeroFlags()
 			&Graphics::flags4,
 			{"AF00.DEF","AF01.DEF","AF02.DEF","AF03.DEF","AF04.DEF",
 			"AF05.DEF","AF06.DEF","AF07.DEF"}
-		}		
+		}
 	};
 
 	#if 0
@@ -314,7 +326,7 @@ void Graphics::loadHeroFlags()
 		loadHeroFlagsDetail(p,true);
 	}
 	#endif
-    logGlobal->infoStream() << "Loading and transforming heroes' flags: "<<th.getDiff();
+	logGlobal->infoStream() << "Loading and transforming heroes' flags: "<<th.getDiff();
 }
 
 void Graphics::blueToPlayersAdv(SDL_Surface * sur, PlayerColor player)
@@ -332,7 +344,7 @@ void Graphics::blueToPlayersAdv(SDL_Surface * sur, PlayerColor player)
 		}
 		else
 		{
-            logGlobal->errorStream() << "Wrong player id in blueToPlayersAdv (" << player << ")!";
+			logGlobal->errorStream() << "Wrong player id in blueToPlayersAdv (" << player << ")!";
 			return;
 		}
 		SDL_SetColors(sur, palette, 224, 32);
@@ -372,11 +384,21 @@ void Graphics::loadFonts()
 
 CDefEssential * Graphics::getDef( const CGObjectInstance * obj )
 {
+	if (obj->appearance.animationFile.empty())
+	{
+		logGlobal->warnStream() << boost::format("Def name for obj %d (%d,%d) is empty!") % obj->id % obj->ID % obj->subID;
+		return nullptr;
+	}
 	return advmapobjGraphics[obj->appearance.animationFile];
 }
 
 CDefEssential * Graphics::getDef( const ObjectTemplate & info )
 {
+	if (info.animationFile.empty())
+	{
+		logGlobal->warnStream() << boost::format("Def name for obj (%d,%d) is empty!") % info.id % info.subid;
+		return nullptr;
+	}
 	return advmapobjGraphics[info.animationFile];
 }
 
@@ -448,12 +470,12 @@ void Graphics::initializeImageLists()
 			addImageListEntry(info.icons[1][1] + 2, "ITPA", info.iconSmall[1][1]);
 		}
 	}
-	
+
 	for(const CSpell * spell : CGI->spellh->objects)
 	{
 		addImageListEntry(spell->id, "SPELLS", spell->iconBook);
 		addImageListEntry(spell->id+1, "SPELLINT", spell->iconEffect);
 		addImageListEntry(spell->id, "SPELLBON", spell->iconScenarioBonus);
-		addImageListEntry(spell->id, "SPELLSCR", spell->iconScroll);		
+		addImageListEntry(spell->id, "SPELLSCR", spell->iconScroll);
 	}
 }

+ 18 - 15
client/Graphics.h

@@ -38,20 +38,30 @@ class Graphics
 {
 	void addImageListEntry(size_t index, std::string listName, std::string imageName);
 
+	void initializeBattleGraphics();
+	void loadPaletteAndColors();
+	void loadHeroFlags();
+	void loadHeroFlagsDetail(std::pair<std::vector<CDefEssential *> Graphics::*, std::vector<const char *> > &pr, bool mode);
+	void loadHeroAnims();
+	CDefEssential *  loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations);
+	void loadErmuToPicture();
+
+	void loadFonts();
+	void initializeImageLists();
+
 public:
 	//Fonts
 	static const int FONTS_NUMBER = 9;
 	IFont * fonts[FONTS_NUMBER];
-	
+
 	//various graphics
 	SDL_Color * playerColors; //array [8]
 	SDL_Color * neutralColor;
 	SDL_Color * playerColorPalette; //palette to make interface colors good - array of size [256]
-	SDL_Color * neutralColorPalette; 
+	SDL_Color * neutralColorPalette;
 
 	std::vector<CDefEssential *> flags1, flags2, flags3, flags4; //flags blitted on heroes when ,
-	CDefEssential * resources32; //resources 32x32
-	CDefEssential * heroMoveArrows;
+	std::shared_ptr<CAnimation> heroMoveArrows;
 	std::map<std::string, CDefEssential *> heroAnims; // [hero class def name]  //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
 	std::vector<CDefEssential *> boatAnims; // [boat type: 0 - 3]  //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
 	CDefHandler * FoWfullHide; //for Fog of War
@@ -68,18 +78,11 @@ public:
 	std::vector< std::vector< std::string > > battleBacks; //battleBacks[terType] - vector of possible names for certain terrain type
 	std::map< int, std::vector < std::string > > battleACToDef; //maps AC format to vector of appropriate def names
 	//functions
-	Graphics();	
-	void initializeBattleGraphics();
-	void loadPaletteAndColors();
-	void loadHeroFlags();
-	void loadHeroFlagsDetail(std::pair<std::vector<CDefEssential *> Graphics::*, std::vector<const char *> > &pr, bool mode);
-	void loadHeroAnims();
-	CDefEssential *  loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations);
-	void loadErmuToPicture();
-	void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player
+	Graphics();
 
-	void loadFonts();
-	void initializeImageLists();
+	void load();
+
+	void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player
 };
 
 extern Graphics * graphics;

+ 125 - 116
client/NetPacksClient.cpp

@@ -2,12 +2,13 @@
 #include "../lib/NetPacks.h"
 
 #include "../lib/filesystem/Filesystem.h"
-#include "../lib/filesystem/CFileInfo.h"
+#include "../lib/filesystem/FileInfo.h"
 #include "../CCallback.h"
 #include "Client.h"
 #include "CPlayerInterface.h"
 #include "CGameInfo.h"
-#include "../lib/Connection.h"
+#include "../lib/serializer/Connection.h"
+#include "../lib/serializer/BinarySerializer.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/VCMI_Lib.h"
@@ -25,6 +26,7 @@
 #include "../lib/CGameState.h"
 #include "../lib/BattleState.h"
 #include "../lib/GameConstants.h"
+#include "../lib/CPlayerState.h"
 #include "gui/CGuiHandler.h"
 #include "widgets/MiscWidgets.h"
 #include "widgets/AdventureMapClasses.h"
@@ -104,39 +106,39 @@
  *
  */
 
-void SetResources::applyCl( CClient *cl )
+void SetResources::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(player,receivedResource,-1,-1);
 }
 
-void SetResource::applyCl( CClient *cl )
+void SetResource::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(player,receivedResource,resid,val);
 }
 
-void SetPrimSkill::applyCl( CClient *cl )
+void SetPrimSkill::applyCl(CClient *cl)
 {
 	const CGHeroInstance *h = cl->getHero(id);
 	if(!h)
 	{
-        logNetwork->errorStream() << "Cannot find hero with ID " << id.getNum();
+		logNetwork->errorStream() << "Cannot find hero with ID " << id.getNum();
 		return;
 	}
 	INTERFACE_CALL_IF_PRESENT(h->tempOwner,heroPrimarySkillChanged,h,which,val);
 }
 
-void SetSecSkill::applyCl( CClient *cl )
+void SetSecSkill::applyCl(CClient *cl)
 {
 	const CGHeroInstance *h = cl->getHero(id);
 	if(!h)
 	{
-        logNetwork->errorStream() << "Cannot find hero with ID " << id;
+		logNetwork->errorStream() << "Cannot find hero with ID " << id;
 		return;
 	}
 	INTERFACE_CALL_IF_PRESENT(h->tempOwner,heroSecondarySkillChanged,h,which,val);
 }
 
-void HeroVisitCastle::applyCl( CClient *cl )
+void HeroVisitCastle::applyCl(CClient *cl)
 {
 	const CGHeroInstance *h = cl->getHero(hid);
 
@@ -146,25 +148,25 @@ void HeroVisitCastle::applyCl( CClient *cl )
 	}
 }
 
-void ChangeSpells::applyCl( CClient *cl )
+void ChangeSpells::applyCl(CClient *cl)
 {
 	//TODO: inform interface?
 }
 
-void SetMana::applyCl( CClient *cl )
+void SetMana::applyCl(CClient *cl)
 {
 	const CGHeroInstance *h = cl->getHero(hid);
 	INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroManaPointsChanged, h);
 }
 
-void SetMovePoints::applyCl( CClient *cl )
+void SetMovePoints::applyCl(CClient *cl)
 {
 	const CGHeroInstance *h = cl->getHero(hid);
 	cl->invalidatePaths();
 	INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroMovePointsChanged, h);
 }
 
-void FoWChange::applyCl( CClient *cl )
+void FoWChange::applyCl(CClient *cl)
 {
 	for(auto &i : cl->playerint)
 	{
@@ -183,85 +185,85 @@ void FoWChange::applyCl( CClient *cl )
 	cl->invalidatePaths();
 }
 
-void SetAvailableHeroes::applyCl( CClient *cl )
+void SetAvailableHeroes::applyCl(CClient *cl)
 {
 	//TODO: inform interface?
 }
 
-void ChangeStackCount::applyCl( CClient *cl )
+void ChangeStackCount::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner, stackChagedCount, sl, count, absoluteValue);
 }
 
-void SetStackType::applyCl( CClient *cl )
+void SetStackType::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner, stackChangedType, sl, *type);
 }
 
-void EraseStack::applyCl( CClient *cl )
+void EraseStack::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner, stacksErased, sl);
 }
 
-void SwapStacks::applyCl( CClient *cl )
+void SwapStacks::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(sl1.army->tempOwner, stacksSwapped, sl1, sl2);
 	if(sl1.army->tempOwner != sl2.army->tempOwner)
 		INTERFACE_CALL_IF_PRESENT(sl2.army->tempOwner, stacksSwapped, sl1, sl2);
 }
 
-void InsertNewStack::applyCl( CClient *cl )
+void InsertNewStack::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,newStackInserted,sl, *sl.getStack());
 }
 
-void RebalanceStacks::applyCl( CClient *cl )
+void RebalanceStacks::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(src.army->tempOwner, stacksRebalanced, src, dst, count);
 	if(src.army->tempOwner != dst.army->tempOwner)
 		INTERFACE_CALL_IF_PRESENT(dst.army->tempOwner,stacksRebalanced, src, dst, count);
 }
 
-void PutArtifact::applyCl( CClient *cl )
+void PutArtifact::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(al.owningPlayer(), artifactPut, al);
 }
 
-void EraseArtifact::applyCl( CClient *cl )
+void EraseArtifact::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(al.owningPlayer(), artifactRemoved, al);
 }
 
-void MoveArtifact::applyCl( CClient *cl )
+void MoveArtifact::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(src.owningPlayer(), artifactMoved, src, dst);
 	if(src.owningPlayer() != dst.owningPlayer())
 		INTERFACE_CALL_IF_PRESENT(dst.owningPlayer(), artifactMoved, src, dst);
 }
 
-void AssembledArtifact::applyCl( CClient *cl )
+void AssembledArtifact::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(al.owningPlayer(), artifactAssembled, al);
 }
 
-void DisassembledArtifact::applyCl( CClient *cl )
+void DisassembledArtifact::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(al.owningPlayer(), artifactDisassembled, al);
 }
 
-void HeroVisit::applyCl( CClient *cl )
+void HeroVisit::applyCl(CClient *cl)
 {
 	assert(hero);
 	INTERFACE_CALL_IF_PRESENT(player, heroVisit, hero, obj, starting);
 }
 
-void NewTurn::applyCl( CClient *cl )
+void NewTurn::applyCl(CClient *cl)
 {
 	cl->invalidatePaths();
 }
 
 
-void GiveBonus::applyCl( CClient *cl )
+void GiveBonus::applyCl(CClient *cl)
 {
 	cl->invalidatePaths();
 	switch(who)
@@ -281,13 +283,13 @@ void GiveBonus::applyCl( CClient *cl )
 	}
 }
 
-void ChangeObjPos::applyFirstCl( CClient *cl )
+void ChangeObjPos::applyFirstCl(CClient *cl)
 {
 	CGObjectInstance *obj = GS(cl)->getObjInstance(objid);
 	if(flags & 1)
 		CGI->mh->hideObject(obj);
 }
-void ChangeObjPos::applyCl( CClient *cl )
+void ChangeObjPos::applyCl(CClient *cl)
 {
 	CGObjectInstance *obj = GS(cl)->getObjInstance(objid);
 	if(flags & 1)
@@ -296,12 +298,12 @@ void ChangeObjPos::applyCl( CClient *cl )
 	cl->invalidatePaths();
 }
 
-void PlayerEndsGame::applyCl( CClient *cl )
+void PlayerEndsGame::applyCl(CClient *cl)
 {
 	CALL_IN_ALL_INTERFACES(gameOver, player, victoryLossCheckResult);
 }
 
-void RemoveBonus::applyCl( CClient *cl )
+void RemoveBonus::applyCl(CClient *cl)
 {
 	cl->invalidatePaths();
 	switch(who)
@@ -321,7 +323,7 @@ void RemoveBonus::applyCl( CClient *cl )
 	}
 }
 
-void UpdateCampaignState::applyCl( CClient *cl )
+void UpdateCampaignState::applyCl(CClient *cl)
 {
 	cl->stopConnection();
 	cl->campaignMapFinished(camp);
@@ -332,7 +334,7 @@ void PrepareForAdvancingCampaign::applyCl(CClient *cl)
 	cl->serv->prepareForSendingHeroes();
 }
 
-void RemoveObject::applyFirstCl( CClient *cl )
+void RemoveObject::applyFirstCl(CClient *cl)
 {
 	const CGObjectInstance *o = cl->getObj(id);
 
@@ -346,12 +348,12 @@ void RemoveObject::applyFirstCl( CClient *cl )
 	}
 }
 
-void RemoveObject::applyCl( CClient *cl )
+void RemoveObject::applyCl(CClient *cl)
 {
 	cl->invalidatePaths();
 }
 
-void TryMoveHero::applyFirstCl( CClient *cl )
+void TryMoveHero::applyFirstCl(CClient *cl)
 {
 	CGHeroInstance *h = GS(cl)->getHero(id);
 
@@ -374,7 +376,7 @@ void TryMoveHero::applyFirstCl( CClient *cl )
 		CGI->mh->printObject(h->boat);
 }
 
-void TryMoveHero::applyCl( CClient *cl )
+void TryMoveHero::applyCl(CClient *cl)
 {
 	const CGHeroInstance *h = cl->getHero(id);
 	cl->invalidatePaths();
@@ -410,13 +412,11 @@ void TryMoveHero::applyCl( CClient *cl )
 	}
 }
 
-void NewStructures::applyCl( CClient *cl )
+void NewStructures::applyCl(CClient *cl)
 {
 	CGTownInstance *town = GS(cl)->getTown(tid);
 	for(const auto & id : bid)
 	{
-		town->updateAppearance();
-
 		if(vstd::contains(cl->playerint,town->tempOwner))
 			cl->playerint[town->tempOwner]->buildChanged(town,id,1);
 	}
@@ -426,14 +426,12 @@ void RazeStructures::applyCl (CClient *cl)
 	CGTownInstance *town = GS(cl)->getTown(tid);
 	for(const auto & id : bid)
 	{
-		town->updateAppearance();
-
 		if(vstd::contains (cl->playerint,town->tempOwner))
 			cl->playerint[town->tempOwner]->buildChanged (town,id,2);
 	}
 }
 
-void SetAvailableCreatures::applyCl( CClient *cl )
+void SetAvailableCreatures::applyCl(CClient *cl)
 {
 	const CGDwelling *dw = static_cast<const CGDwelling*>(cl->getObj(tid));
 
@@ -448,28 +446,28 @@ void SetAvailableCreatures::applyCl( CClient *cl )
 	INTERFACE_CALL_IF_PRESENT(p, availableCreaturesChanged, dw);
 }
 
-void SetHeroesInTown::applyCl( CClient *cl )
+void SetHeroesInTown::applyCl(CClient *cl)
 {
 	CGTownInstance *t = GS(cl)->getTown(tid);
 	CGHeroInstance *hGarr  = GS(cl)->getHero(this->garrison);
 	CGHeroInstance *hVisit = GS(cl)->getHero(this->visiting);
 
-	std::set<PlayerColor> playersToNotify;
-
-	if(vstd::contains(cl->playerint,t->tempOwner)) // our town
-		playersToNotify.insert(t->tempOwner);
-
-	if (hGarr && vstd::contains(cl->playerint,  hGarr->tempOwner))
-		playersToNotify.insert(hGarr->tempOwner);
-
-	if (hVisit && vstd::contains(cl->playerint, hVisit->tempOwner))
-		playersToNotify.insert(hVisit->tempOwner);
+	//inform all players that see this object
+	for(auto i = cl->playerint.cbegin(); i != cl->playerint.cend(); ++i)
+	{
+		if(i->first >= PlayerColor::PLAYER_LIMIT)
+			continue;
 
-	for(auto playerID : playersToNotify)
-		cl->playerint[playerID]->heroInGarrisonChange(t);
+		if(GS(cl)->isVisible(t, i->first) ||
+			(hGarr && GS(cl)->isVisible(hGarr, i->first)) ||
+			(hVisit && GS(cl)->isVisible(hVisit, i->first)))
+		{
+			cl->playerint[i->first]->heroInGarrisonChange(t);
+		}
+	}
 }
 
-// void SetHeroArtifacts::applyCl( CClient *cl )
+// void SetHeroArtifacts::applyCl(CClient *cl)
 // {
 // // 	CGHeroInstance *h = GS(cl)->getHero(hid);
 // // 	CGameInterface *player = (vstd::contains(cl->playerint,h->tempOwner) ? cl->playerint[h->tempOwner] : nullptr);
@@ -489,37 +487,43 @@ void SetHeroesInTown::applyCl( CClient *cl )
 // // 	}
 // }
 
-void HeroRecruited::applyCl( CClient *cl )
+void HeroRecruited::applyCl(CClient *cl)
 {
 	CGHeroInstance *h = GS(cl)->map->heroesOnMap.back();
 	if(h->subID != hid)
 	{
-        logNetwork->errorStream() << "Something wrong with hero recruited!";
+		logNetwork->errorStream() << "Something wrong with hero recruited!";
 	}
 
-	CGI->mh->printObject(h);
-
-	if(vstd::contains(cl->playerint,h->tempOwner))
+	bool needsPrinting = true;
+	if(vstd::contains(cl->playerint, h->tempOwner))
 	{
 		cl->playerint[h->tempOwner]->heroCreated(h);
 		if(const CGTownInstance *t = GS(cl)->getTown(tid))
+		{
 			cl->playerint[h->tempOwner]->heroInGarrisonChange(t);
+			needsPrinting = false;
+		}
+	}
+	if (needsPrinting)
+	{
+		CGI->mh->printObject(h);
 	}
 }
 
-void GiveHero::applyCl( CClient *cl )
+void GiveHero::applyCl(CClient *cl)
 {
 	CGHeroInstance *h = GS(cl)->getHero(id);
 	CGI->mh->printObject(h);
 	cl->playerint[h->tempOwner]->heroCreated(h);
 }
 
-void GiveHero::applyFirstCl( CClient *cl )
+void GiveHero::applyFirstCl(CClient *cl)
 {
 	CGI->mh->hideObject(GS(cl)->getHero(id));
 }
 
-void InfoWindow::applyCl( CClient *cl )
+void InfoWindow::applyCl(CClient *cl)
 {
 	std::vector<Component*> comps;
 	for(auto & elem : components)
@@ -532,10 +536,10 @@ void InfoWindow::applyCl( CClient *cl )
 	if(vstd::contains(cl->playerint,player))
 		cl->playerint.at(player)->showInfoDialog(str,comps,(soundBase::soundID)soundID);
 	else
-        logNetwork->warnStream() << "We received InfoWindow for not our player...";
+		logNetwork->warnStream() << "We received InfoWindow for not our player...";
 }
 
-void SetObjectProperty::applyCl( CClient *cl )
+void SetObjectProperty::applyCl(CClient *cl)
 {
 	//inform all players that see this object
 	for(auto it = cl->playerint.cbegin(); it != cl->playerint.cend(); ++it)
@@ -545,7 +549,7 @@ void SetObjectProperty::applyCl( CClient *cl )
 	}
 }
 
-void HeroLevelUp::applyCl( CClient *cl )
+void HeroLevelUp::applyCl(CClient *cl)
 {
 	//INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroGotLevel, h, primskill, skills, id);
 	if(vstd::contains(cl->playerint,hero->tempOwner))
@@ -556,7 +560,7 @@ void HeroLevelUp::applyCl( CClient *cl )
 	//	cb->selectionMade(0, queryID);
 }
 
-void CommanderLevelUp::applyCl( CClient *cl )
+void CommanderLevelUp::applyCl(CClient *cl)
 {
 	const CCommanderInstance * commander = hero->commander;
 	assert (commander);
@@ -567,7 +571,7 @@ void CommanderLevelUp::applyCl( CClient *cl )
 	}
 }
 
-void BlockingDialog::applyCl( CClient *cl )
+void BlockingDialog::applyCl(CClient *cl)
 {
 	std::string str;
 	text.toString(str);
@@ -575,7 +579,7 @@ void BlockingDialog::applyCl( CClient *cl )
 	if(vstd::contains(cl->playerint,player))
 		cl->playerint.at(player)->showBlockingDialog(str,components,queryID,(soundBase::soundID)soundID,selection(),cancel());
 	else
-        logNetwork->warnStream() << "We received YesNoDialog for not our player...";
+		logNetwork->warnStream() << "We received YesNoDialog for not our player...";
 }
 
 void GarrisonDialog::applyCl(CClient *cl)
@@ -595,12 +599,12 @@ void ExchangeDialog::applyCl(CClient *cl)
 	INTERFACE_CALL_IF_PRESENT(heroes[0]->tempOwner, heroExchangeStarted, heroes[0]->id, heroes[1]->id, queryID);
 }
 
-void TeleportDialog::applyCl( CClient *cl )
+void TeleportDialog::applyCl(CClient *cl)
 {
 	CALL_ONLY_THAT_INTERFACE(hero->tempOwner,showTeleportDialog,channel,exits,impassable,queryID);
 }
 
-void BattleStart::applyFirstCl( CClient *cl )
+void BattleStart::applyFirstCl(CClient *cl)
 {
 	//Cannot use the usual macro because curB is not set yet
 	CALL_ONLY_THAT_BATTLE_INTERFACE(info->sides[0].color, battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
@@ -611,7 +615,7 @@ void BattleStart::applyFirstCl( CClient *cl )
 		info->tile, info->sides[0].hero, info->sides[1].hero);
 }
 
-void BattleStart::applyCl( CClient *cl )
+void BattleStart::applyCl(CClient *cl)
 {
 	cl->battleStarted(info);
 }
@@ -621,30 +625,30 @@ void BattleNextRound::applyFirstCl(CClient *cl)
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewRoundFirst,round);
 }
 
-void BattleNextRound::applyCl( CClient *cl )
+void BattleNextRound::applyCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewRound,round);
 }
 
-void BattleSetActiveStack::applyCl( CClient *cl )
+void BattleSetActiveStack::applyCl(CClient *cl)
 {
 	if(!askPlayerInterface)
 		return;
 
-	const CStack * activated = GS(cl)->curB->battleGetStackByID(stack);
+	const CStack *activated = GS(cl)->curB->battleGetStackByID(stack);
 	PlayerColor playerToCall; //player that will move activated stack
-	if( activated->hasBonusOfType(Bonus::HYPNOTIZED) )
+	if (activated->hasBonusOfType(Bonus::HYPNOTIZED))
 	{
-		playerToCall = ( GS(cl)->curB->sides[0].color == activated->owner 
-			? GS(cl)->curB->sides[1].color 
-			: GS(cl)->curB->sides[0].color );
+		playerToCall = (GS(cl)->curB->sides[0].color == activated->owner
+			? GS(cl)->curB->sides[1].color
+			: GS(cl)->curB->sides[0].color);
 	}
 	else
 	{
 		playerToCall = activated->owner;
 	}
-	if( vstd::contains(cl->battleints, playerToCall) )
-		boost::thread( std::bind(&CClient::waitForMoveAndSend, cl, playerToCall) );
+	if (vstd::contains(cl->battleints, playerToCall))
+		boost::thread(std::bind(&CClient::waitForMoveAndSend, cl, playerToCall));
 }
 
 void BattleTriggerEffect::applyCl(CClient * cl)
@@ -657,20 +661,25 @@ void BattleObstaclePlaced::applyCl(CClient * cl)
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleObstaclePlaced, *obstacle);
 }
 
-void BattleResult::applyFirstCl( CClient *cl )
+void BattleUpdateGateState::applyFirstCl(CClient * cl)
+{
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleGateStateChanged, state);
+}
+
+void BattleResult::applyFirstCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleEnd,this);
 	cl->battleFinished();
 }
 
-void BattleStackMoved::applyFirstCl( CClient *cl )
+void BattleStackMoved::applyFirstCl(CClient *cl)
 {
 	const CStack * movedStack = GS(cl)->curB->battleGetStackByID(stack);
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStackMoved,movedStack,tilesToMove,distance);
 }
 
-//void BattleStackAttacked::( CClient *cl )
-void BattleStackAttacked::applyFirstCl( CClient *cl )
+//void BattleStackAttacked::(CClient *cl)
+void BattleStackAttacked::applyFirstCl(CClient *cl)
 {
 	std::vector<BattleStackAttacked> bsa;
 	bsa.push_back(*this);
@@ -678,7 +687,7 @@ void BattleStackAttacked::applyFirstCl( CClient *cl )
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,bsa);
 }
 
-void BattleAttack::applyFirstCl( CClient *cl )
+void BattleAttack::applyFirstCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleAttack,this);
 	for (auto & elem : bsa)
@@ -690,34 +699,34 @@ void BattleAttack::applyFirstCl( CClient *cl )
 	}
 }
 
-void BattleAttack::applyCl( CClient *cl )
+void BattleAttack::applyCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,bsa);
 }
 
-void StartAction::applyFirstCl( CClient *cl )
+void StartAction::applyFirstCl(CClient *cl)
 {
 	cl->curbaction = ba;
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionStarted, ba);
 }
 
-void BattleSpellCast::applyCl( CClient *cl )
+void BattleSpellCast::applyCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleSpellCast,this);
 }
 
-void SetStackEffect::applyCl( CClient *cl )
+void SetStackEffect::applyCl(CClient *cl)
 {
 	//informing about effects
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksEffectsSet,*this);
 }
 
-void StacksInjured::applyCl( CClient *cl )
+void StacksInjured::applyCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,stacks);
 }
 
-void BattleResultsApplied::applyCl( CClient *cl )
+void BattleResultsApplied::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(player1, battleResultsApplied);
 	INTERFACE_CALL_IF_PRESENT(player2, battleResultsApplied);
@@ -728,7 +737,7 @@ void BattleResultsApplied::applyCl( CClient *cl )
 	}
 }
 
-void StacksHealedOrResurrected::applyCl( CClient *cl )
+void StacksHealedOrResurrected::applyCl(CClient *cl)
 {
 	std::vector<std::pair<ui32, ui32> > shiftedHealed;
 	for(auto & elem : healedStacks)
@@ -738,63 +747,63 @@ void StacksHealedOrResurrected::applyCl( CClient *cl )
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksHealedRes, shiftedHealed, lifeDrain, tentHealing, drainedFrom);
 }
 
-void ObstaclesRemoved::applyCl( CClient *cl )
+void ObstaclesRemoved::applyCl(CClient *cl)
 {
 	//inform interfaces about removed obstacles
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleObstaclesRemoved, obstacles);
 }
 
-void CatapultAttack::applyCl( CClient *cl )
+void CatapultAttack::applyCl(CClient *cl)
 {
 	//inform interfaces about catapult attack
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleCatapultAttacked, *this);
 }
 
-void BattleStacksRemoved::applyCl( CClient *cl )
+void BattleStacksRemoved::applyFirstCl(CClient * cl)
 {
 	//inform interfaces about removed stacks
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksRemoved, *this);
 }
 
-void BattleStackAdded::applyCl( CClient *cl )
+void BattleStackAdded::applyCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewStackAppeared, GS(cl)->curB->stacks.back());
 }
 
-CGameState* CPackForClient::GS( CClient *cl )
+CGameState* CPackForClient::GS(CClient *cl)
 {
 	return cl->gs;
 }
 
-void EndAction::applyCl( CClient *cl )
+void EndAction::applyCl(CClient *cl)
 {
 	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionFinished, *cl->curbaction);
 	cl->curbaction.reset();
 }
 
-void PackageApplied::applyCl( CClient *cl )
+void PackageApplied::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(player, requestRealized, this);
 	if(!cl->waitingRequest.tryRemovingElement(requestID))
-        logNetwork->warnStream() << "Surprising server message!";
+		logNetwork->warnStream() << "Surprising server message!";
 }
 
-void SystemMessage::applyCl( CClient *cl )
+void SystemMessage::applyCl(CClient *cl)
 {
 	std::ostringstream str;
 	str << "System message: " << text;
 
-    logNetwork->errorStream() << str.str(); // usually used to receive error messages from server
-	if(LOCPLINT)
+	logNetwork->errorStream() << str.str(); // usually used to receive error messages from server
+	if(LOCPLINT && !settings["session"]["hideSystemMessages"].Bool())
 		LOCPLINT->cingconsole->print(str.str());
 }
 
-void PlayerBlocked::applyCl( CClient *cl )
+void PlayerBlocked::applyCl(CClient *cl)
 {
 	INTERFACE_CALL_IF_PRESENT(player,playerBlocked,reason, startOrEnd==BLOCKADE_STARTED);
 }
 
-void YourTurn::applyCl( CClient *cl )
+void YourTurn::applyCl(CClient *cl)
 {
 	CALL_IN_ALL_INTERFACES(playerStartsTurn, player);
 	CALL_ONLY_THAT_INTERFACE(player,yourTurn);
@@ -802,27 +811,27 @@ void YourTurn::applyCl( CClient *cl )
 
 void SaveGame::applyCl(CClient *cl)
 {
-	CFileInfo info(fname);
-	CResourceHandler::get("local")->createResource(info.getStem() + ".vcgm1");
+	const auto stem = FileInfo::GetPathStem(fname);
+	CResourceHandler::get("local")->createResource(stem.to_string() + ".vcgm1");
 
 	try
 	{
-		CSaveFile save(*CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME)));
+		CSaveFile save(*CResourceHandler::get()->getResourceName(ResourceID(stem.to_string(), EResType::CLIENT_SAVEGAME)));
 		cl->saveCommonState(save);
 		save << *cl;
 	}
 	catch(std::exception &e)
 	{
-        logNetwork->errorStream() << "Failed to save game:" << e.what();
+		logNetwork->errorStream() << "Failed to save game:" << e.what();
 	}
 }
 
 void PlayerMessage::applyCl(CClient *cl)
 {
-	std::ostringstream str;
-	str << "Player "<< player <<" sends a message: " << text;
+	logNetwork->debugStream() << "Player "<< player <<" sends a message: " << text;
 
-    logNetwork->debugStream() << str.str();
+	std::ostringstream str;
+	str << cl->getPlayer(player)->nodeName() <<": " << text;
 	if(LOCPLINT)
 		LOCPLINT->cingconsole->print(str.str());
 }
@@ -955,6 +964,6 @@ void TradeComponents::applyCl(CClient *cl)
 	case Obj::TRADING_POST_SNOW:
 		break;
 	default:
-        logNetwork->warnStream() << "Shop type not supported!";
+		logNetwork->warnStream() << "Shop type not supported!";
 	}
 }

+ 95 - 0
client/SDLRWwrapper.cpp

@@ -0,0 +1,95 @@
+/*
+ * SDLRWwrapper.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "SDLRWwrapper.h"
+#include "../lib/filesystem/CInputStream.h"
+#include <SDL_rwops.h>
+
+static inline CInputStream* get_stream(SDL_RWops* context)
+{
+	return static_cast<CInputStream*>(context->hidden.unknown.data1);
+}
+
+static Sint64 impl_size(SDL_RWops* context)
+{
+    return get_stream(context)->getSize();
+}
+
+static Sint64 impl_seek(SDL_RWops* context, Sint64 offset, int whence)
+{
+	auto stream = get_stream(context);
+	switch (whence)
+	{
+	case RW_SEEK_SET:
+		return stream->seek(offset);
+		break;
+	case RW_SEEK_CUR:
+		return stream->seek(stream->tell() + offset);
+		break;
+	case RW_SEEK_END:
+		return stream->seek(stream->getSize() + offset);
+		break;
+	default:
+		return -1;
+	}
+
+}
+
+static std::size_t impl_read(SDL_RWops* context, void *ptr, size_t size, size_t maxnum)
+{
+    auto stream = get_stream(context);
+    auto oldpos = stream->tell();
+
+    auto count = stream->read(static_cast<ui8*>(ptr), size*maxnum);
+
+	if (count != 0 && count != size*maxnum)
+	{
+		// if not a whole amount of objects of size has been read, we need to seek
+		stream->seek(oldpos + size * (count / size));
+	}
+
+    return count / size;
+}
+
+static std::size_t impl_write(SDL_RWops* context, const void *ptr, size_t size, size_t num)
+{
+	// writing is not supported
+    return 0;
+}
+
+static int impl_close(SDL_RWops* context)
+{
+	if (context == nullptr)
+		return 0;
+
+    delete get_stream(context);
+    SDL_FreeRW(context);
+    return 0;
+}
+
+
+SDL_RWops* MakeSDLRWops(std::unique_ptr<CInputStream> in)
+{
+	SDL_RWops* result = SDL_AllocRW();
+	if (!result)
+		return nullptr;
+
+	result->size  = &impl_size;
+	result->seek  = &impl_seek;
+	result->read  = &impl_read;
+	result->write = &impl_write;
+	result->close = &impl_close;
+
+	result->type = SDL_RWOPS_UNKNOWN;
+	result->hidden.unknown.data1 = in.release();
+
+	return result;
+}

+ 16 - 0
client/SDLRWwrapper.h

@@ -0,0 +1,16 @@
+#pragma once
+
+/*
+ * SDLRWwrapper.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+struct SDL_RWops;
+class CInputStream;
+
+SDL_RWops* MakeSDLRWops(std::unique_ptr<CInputStream> in);

+ 27 - 49
client/VCMI_client.cbp

@@ -6,105 +6,78 @@
 		<Option pch_mode="2" />
 		<Option compiler="gcc" />
 		<Build>
-			<Target title="Debug-win32-SDL2">
+			<Target title="Debug-win32">
 				<Option platforms="Windows;" />
 				<Option output="../VCMI_client" prefix_auto="1" extension_auto="1" />
 				<Option working_dir="../" />
-				<Option object_output="../obj/Debug/" />
+				<Option object_output="../obj/Client/Debug/x86" />
 				<Option type="1" />
 				<Option compiler="gcc" />
 				<Compiler>
 					<Add option="-Og" />
 					<Add option="-g" />
-					<Add directory="$(#sdl2.include)" />
 				</Compiler>
 				<Linker>
-					<Add option="-lSDL2.dll" />
-					<Add option="-lSDL2_image.dll" />
-					<Add option="-lSDL2_mixer.dll" />
-					<Add option="-lSDL2_ttf.dll" />
 					<Add directory="$(#sdl2.lib)" />
+					<Add directory="$(#boost.lib32)" />
+					<Add directory="$(#ffmpeg.lib32)" />
 				</Linker>
 			</Target>
-			<Target title="Release-win32-SDL2">
+			<Target title="Release-win32">
 				<Option platforms="Windows;" />
 				<Option output="../VCMI_client" prefix_auto="1" extension_auto="1" />
 				<Option working_dir="../" />
-				<Option object_output="../obj/Release/" />
+				<Option object_output="../obj/Client/Release/x86" />
 				<Option type="1" />
 				<Option compiler="gcc" />
 				<Compiler>
 					<Add option="-fomit-frame-pointer" />
 					<Add option="-O2" />
-					<Add directory="$(#sdl2.include)" />
 				</Compiler>
 				<Linker>
 					<Add option="-s" />
-					<Add option="-lSDL2.dll" />
-					<Add option="-lSDL2_image.dll" />
-					<Add option="-lSDL2_mixer.dll" />
-					<Add option="-lSDL2_ttf.dll" />
 					<Add directory="$(#sdl2.lib)" />
+					<Add directory="$(#boost.lib32)" />
+					<Add directory="$(#ffmpeg.lib32)" />
 				</Linker>
 			</Target>
-			<Target title="Debug-win32-SDL1">
+			<Target title="Debug-win64">
 				<Option platforms="Windows;" />
 				<Option output="../VCMI_client" prefix_auto="1" extension_auto="1" />
 				<Option working_dir="../" />
-				<Option object_output="../obj/Debug/" />
+				<Option object_output="../obj/Client/Debug/x64" />
 				<Option type="1" />
-				<Option compiler="gcc" />
+				<Option compiler="gnu_gcc_compiler_x64" />
 				<Compiler>
 					<Add option="-Og" />
 					<Add option="-g" />
-					<Add directory="$(#sdl.include)" />
-				</Compiler>
-				<Linker>
-					<Add option="-lSDL" />
-					<Add option="-lSDL_image" />
-					<Add option="-lSDL_mixer" />
-					<Add option="-lSDL_ttf" />
-					<Add directory="$(#sdl.lib)" />
-				</Linker>
-			</Target>
-			<Target title="Release-win32-SDL1">
-				<Option platforms="Windows;" />
-				<Option output="../VCMI_client" prefix_auto="1" extension_auto="1" />
-				<Option working_dir="../" />
-				<Option object_output="../obj/Release/" />
-				<Option type="1" />
-				<Option compiler="gcc" />
-				<Compiler>
-					<Add option="-fomit-frame-pointer" />
-					<Add option="-O2" />
-					<Add directory="$(#sdl.include)" />
 				</Compiler>
 				<Linker>
-					<Add option="-s" />
-					<Add option="-lSDL" />
-					<Add option="-lSDL_image" />
-					<Add option="-lSDL_mixer" />
-					<Add option="-lSDL_ttf" />
-					<Add directory="$(#sdl.lib)" />
+					<Add directory="$(#sdl2.lib64)" />
+					<Add directory="$(#boost.lib64)" />
+					<Add directory="$(#ffmpeg.lib64)" />
 				</Linker>
 			</Target>
 		</Build>
 		<Compiler>
 			<Add option="-Wextra" />
 			<Add option="-Wall" />
+			<Add option="-std=gnu++11" />
 			<Add option="-fexceptions" />
 			<Add option="-Wpointer-arith" />
 			<Add option="-Wno-switch" />
 			<Add option="-Wno-sign-compare" />
 			<Add option="-Wno-unused-parameter" />
 			<Add option="-Wno-overloaded-virtual" />
-			<Add option="-fpermissive" />
+			<Add option="-isystem $(#boost.include)" />
 			<Add option="-DBOOST_THREAD_USE_LIB" />
+			<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
 			<Add option="-D_WIN32_WINNT=0x0501" />
-			<Add directory="$(#boost.include)" />
-			<Add directory="../include" />
+			<Add option="-D_WIN32" />
 			<Add directory="../client" />
 			<Add directory="$(#ffmpeg.include)" />
+			<Add directory="$(#sdl2.include)" />
+			<Add directory="../include" />
 		</Compiler>
 		<Linker>
 			<Add option="-lole32" />
@@ -120,9 +93,11 @@
 			<Add option="-lavformat.dll" />
 			<Add option="-lswscale.dll" />
 			<Add option="-lavutil.dll" />
-			<Add directory="$(#boost.lib32)" />
+			<Add option="-lSDL2.dll" />
+			<Add option="-lSDL2_image.dll" />
+			<Add option="-lSDL2_mixer.dll" />
+			<Add option="-lSDL2_ttf.dll" />
 			<Add directory="../" />
-			<Add directory="$(#ffmpeg.lib)" />
 		</Linker>
 		<Unit filename="../CCallback.cpp" />
 		<Unit filename="../CCallback.h" />
@@ -150,7 +125,10 @@
 		<Unit filename="Graphics.h" />
 		<Unit filename="NetPacksClient.cpp" />
 		<Unit filename="SDLMain.h" />
+		<Unit filename="SDLRWwrapper.cpp" />
+		<Unit filename="SDLRWwrapper.h" />
 		<Unit filename="StdInc.h">
+			<Option compile="1" />
 			<Option weight="0" />
 		</Unit>
 		<Unit filename="VCMI_client.rc">

+ 2 - 0
client/VCMI_client.vcxproj

@@ -203,6 +203,7 @@
     <ClCompile Include="gui\SDL_Extensions.cpp" />
     <ClCompile Include="mapHandler.cpp" />
     <ClCompile Include="NetPacksClient.cpp" />
+    <ClCompile Include="SDLRWwrapper.cpp" />
     <ClCompile Include="StdInc.cpp">
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
     </ClCompile>
@@ -256,6 +257,7 @@
     <ClInclude Include="mapHandler.h" />
     <ClInclude Include="resource.h" />
     <ClInclude Include="SDLMain.h" />
+    <ClInclude Include="SDLRWwrapper.h" />
     <ClInclude Include="StdInc.h" />
     <ClInclude Include="widgets\AdventureMapClasses.h" />
     <ClInclude Include="widgets\Buttons.h" />

+ 10 - 6
client/battle/CBattleAnimations.cpp

@@ -233,8 +233,9 @@ std::string CDefenceAnimation::getMySound()
 	if(killed)
 		return battle_sound(stack->getCreature(), killed);
 
-	if (stack->valOfBonuses(Selector::durationType(Bonus::STACK_GETS_TURN)))
+	if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
 		return battle_sound(stack->getCreature(), defend);
+
 	return battle_sound(stack->getCreature(), wince);
 }
 
@@ -242,9 +243,10 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
 {
 	if(killed)
 		return CCreatureAnim::DEATH;
-
-	if (stack->valOfBonuses(Selector::durationType(Bonus::STACK_GETS_TURN).And(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE))))
+	
+	if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
 		return CCreatureAnim::DEFENCE;
+
 	return CCreatureAnim::HITTED;
 }
 
@@ -281,7 +283,9 @@ void CDefenceAnimation::endAnim()
 
 CDummyAnimation::CDummyAnimation(CBattleInterface * _owner, int howManyFrames) 
 : CBattleAnimation(_owner), counter(0), howMany(howManyFrames)
-{}
+{
+	logAnim->debugStream() << "Created dummy animation for " << howManyFrames <<" frames";
+}
 
 bool CDummyAnimation::init()
 {
@@ -357,7 +361,7 @@ bool CMeleeAttackAnimation::init()
 		group = mutPosToGroup[mutPos];
 		break;
 	default:
-        logGlobal->errorStream()<<"Critical Error! Wrong dest in stackAttacking! dest: "<<dest<<" attacking stack pos: "<<attackingStackPosBeforeReturn<<" mutual pos: "<<mutPos;
+		logGlobal->errorStream()<<"Critical Error! Wrong dest in stackAttacking! dest: "<<dest<<" attacking stack pos: "<<attackingStackPosBeforeReturn<<" mutual pos: "<<mutPos;
 		group = CCreatureAnim::ATTACK_FRONT;
 		break;
 	}
@@ -778,7 +782,7 @@ bool CShootingAnimation::init()
 		spi.catapultInfo.reset(new CatapultProjectileInfo(Point(spi.x, spi.y), destPos));
 
 		double animSpeed = AnimationControls::getProjectileSpeed() / 10;
-		spi.lastStep = abs((destPos.x - spi.x) / animSpeed);
+		spi.lastStep = std::abs((destPos.x - spi.x) / animSpeed);
 		spi.dx = animSpeed;
 		spi.dy = 0;
 

+ 25 - 25
client/battle/CBattleAnimations.h

@@ -59,8 +59,8 @@ protected:
 	const CStack *attackingStack;
 	int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
 public:
-	void nextFrame();
-	void endAnim();
+	void nextFrame() override;
+	void endAnim() override;
 	bool checkInitialConditions();
 
 	CAttackAnimation(CBattleInterface *_owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
@@ -80,9 +80,9 @@ class CDefenceAnimation : public CBattleStackAnimation
 
 	float timeToWait; // for how long this animation should be paused
 public:
-	bool init();
-	void nextFrame();
-	void endAnim();
+	bool init() override;
+	void nextFrame() override;
+	void endAnim() override;
 
 	CDefenceAnimation(StackAttackedInfo _attackedInfo, CBattleInterface * _owner);
 	virtual ~CDefenceAnimation(){};
@@ -94,9 +94,9 @@ private:
 	int counter;
 	int howMany;
 public:
-	bool init();
-	void nextFrame();
-	void endAnim();
+	bool init() override;
+	void nextFrame() override;
+	void endAnim() override;
 
 	CDummyAnimation(CBattleInterface * _owner, int howManyFrames);
 	virtual ~CDummyAnimation(){}
@@ -106,8 +106,8 @@ public:
 class CMeleeAttackAnimation : public CAttackAnimation
 {
 public:
-	bool init();
-	void endAnim();
+	bool init() override;
+	void endAnim() override;
 
 	CMeleeAttackAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked);
 	virtual ~CMeleeAttackAnimation(){};
@@ -133,9 +133,9 @@ private:
 public:
 	BattleHex nextHex; // next hex, to which creature move right now
 
-	bool init();
-	void nextFrame();
-	void endAnim();
+	bool init() override;
+	void nextFrame() override;
+	void endAnim() override;
 
 	CMovementAnimation(CBattleInterface *_owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
 	virtual ~CMovementAnimation(){};
@@ -147,8 +147,8 @@ class CMovementEndAnimation : public CBattleStackAnimation
 private:
 	BattleHex destinationTile;
 public:
-	bool init();
-	void endAnim();
+	bool init() override;
+	void endAnim() override;
 
 	CMovementEndAnimation(CBattleInterface * _owner, const CStack * _stack, BattleHex destTile);
 	virtual ~CMovementEndAnimation(){};
@@ -158,8 +158,8 @@ public:
 class CMovementStartAnimation : public CBattleStackAnimation
 {
 public:
-	bool init();
-	void endAnim();
+	bool init() override;
+	void endAnim() override;
 
 	CMovementStartAnimation(CBattleInterface * _owner, const CStack * _stack);
 	virtual ~CMovementStartAnimation(){};
@@ -171,12 +171,12 @@ class CReverseAnimation : public CBattleStackAnimation
 	BattleHex hex;
 public:
 	bool priority; //true - high, false - low
-	bool init();
+	bool init() override;
 
 	static void rotateStack(CBattleInterface * owner, const CStack * stack, BattleHex hex);
 
 	void setupSecondPart();
-	void endAnim();
+	void endAnim() override;
 
 	CReverseAnimation(CBattleInterface * _owner, const CStack * stack, BattleHex dest, bool _priority);
 	virtual ~CReverseAnimation(){};
@@ -204,9 +204,9 @@ class CShootingAnimation : public CAttackAnimation
 private:
 	int catapultDamage;
 public:
-	bool init();
-	void nextFrame();
-	void endAnim();
+	bool init() override;
+	void nextFrame() override;
+	void endAnim() override;
 
 	//last two params only for catapult attacks
 	CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, 
@@ -225,9 +225,9 @@ private:
 	bool Vflip;
 	bool alignToBottom;
 public:
-	bool init();
-	void nextFrame();
-	void endAnim();
+	bool init() override;
+	void nextFrame() override;
+	void endAnim() override;
 
 	CSpellEffectAnimation(CBattleInterface * _owner, ui32 _effect, BattleHex _destTile, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
 	CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 190 - 322
client/battle/CBattleInterface.cpp


+ 119 - 93
client/battle/CBattleInterface.h

@@ -1,12 +1,12 @@
 #pragma once
 
-
-//#include "../../lib/CCreatureSet.h"
 #include "../../lib/ConstTransitivePtr.h" //may be reundant
 #include "../../lib/GameConstants.h"
 
 #include "CBattleAnimations.h"
 
+#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
+
 /*
  * CBattleInterface.h, part of VCMI engine
  *
@@ -53,10 +53,10 @@ class CBattleGameInterface;
 /// Small struct which contains information about the id of the attacked stack, the damage dealt,...
 struct StackAttackedInfo
 {
-	const CStack * defender; //attacked stack
+	const CStack *defender; //attacked stack
 	unsigned int dmg; //damage dealt
 	unsigned int amountKilled; //how many creatures in stack has been killed
-	const CStack * attacker; //attacking stack
+	const CStack *attacker; //attacking stack
 	bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack
 	bool killed; //if true, stack has been killed
 	bool rebirth; //if true, play rebirth animation after all
@@ -69,7 +69,7 @@ struct BattleEffect
 	int x, y; //position on the screen
 	float currentFrame;
 	int maxFrame;
-	CDefHandler * anim; //animation to display
+	CDefHandler *anim; //animation to display
 	int effectID; //uniqueID equal ot ID of appropriate CSpellEffectAnim
 	BattleHex position; //Indicates if effect which hex the effect is drawn on
 };
@@ -77,9 +77,9 @@ struct BattleEffect
 struct BattleObjectsByHex
 {
 	typedef std::vector<int> TWallList;
-	typedef std::vector<const CStack * > TStackList;
+	typedef std::vector<const CStack *> TStackList;
 	typedef std::vector<const BattleEffect *> TEffectList;
-	typedef std::vector<shared_ptr<const CObstacleInstance> > TObstacleList;
+	typedef std::vector<std::shared_ptr<const CObstacleInstance>> TObstacleList;
 
 	struct HexData
 	{
@@ -114,23 +114,24 @@ class CBattleInterface : public CIntObject
 		INVALID = -1, CREATURE_INFO,
 		MOVE_TACTICS, CHOOSE_TACTICS_STACK,
 		MOVE_STACK, ATTACK, WALK_AND_ATTACK, ATTACK_AND_RETURN, SHOOT, //OPEN_GATE, //we can open castle gate during siege
-		NO_LOCATION, ANY_LOCATION, FRIENDLY_CREATURE_SPELL, HOSTILE_CREATURE_SPELL, RISING_SPELL, ANY_CREATURE, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL,
+		NO_LOCATION, ANY_LOCATION, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL,
 		FREE_LOCATION, //used with Force Field and Fire Wall - all tiles affected by spell must be free
-		CATAPULT, HEAL, RISE_DEMONS
+		CATAPULT, HEAL, RISE_DEMONS,
+		AIMED_SPELL_CREATURE
 	};
 private:
-	SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes;
-	CButton * bOptions, * bSurrender, * bFlee, * bAutofight, * bSpell,
-		* bWait, * bDefence, * bConsoleUp, * bConsoleDown, *btactNext, *btactEnd;
-	CBattleConsole * console;
-	CBattleHero * attackingHero, * defendingHero; //fighting heroes
+	SDL_Surface *background, *menu, *amountNormal, *amountNegative, *amountPositive, *amountEffNeutral, *cellBorders, *backgroundWithHexes;
+	CButton *bOptions, *bSurrender, *bFlee, *bAutofight, *bSpell,
+		* bWait, *bDefence, *bConsoleUp, *bConsoleDown, *btactNext, *btactEnd;
+	CBattleConsole *console;
+	CBattleHero *attackingHero, *defendingHero; //fighting heroes
 	CStackQueue *queue;
 	const CCreatureSet *army1, *army2; //copy of initial armies (for result window)
-	const CGHeroInstance * attackingHeroInstance, * defendingHeroInstance;
-	std::map< int, CCreatureAnimation * > creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
-	std::map< int, CDefHandler * > idToProjectile; //projectiles of creatures (creatureID, defhandler)
-	std::map< int, CDefHandler * > idToObstacle; //obstacles located on the battlefield
-	std::map< int, SDL_Surface * > idToAbsoluteObstacle; //obstacles located on the battlefield
+	const CGHeroInstance *attackingHeroInstance, *defendingHeroInstance;
+	std::map<int, CCreatureAnimation *> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
+	std::map<int, CDefHandler *> idToProjectile; //projectiles of creatures (creatureID, defhandler)
+	std::map<int, CDefHandler *> idToObstacle; //obstacles located on the battlefield
+	std::map<int, SDL_Surface *> idToAbsoluteObstacle; //obstacles located on the battlefield
 
 	//TODO these should be loaded only when needed (and freed then) but I believe it's rather work for resource manager,
 	//so I didn't implement that (having ongoing RM development)
@@ -139,28 +140,27 @@ private:
 	CDefHandler *fireWall;
 	CDefHandler *smallForceField[2], *bigForceField[2]; // [side]
 
-	std::map< int, bool > creDir; // <creatureID, if false reverse creature's animation> //TODO: move it to battle callback
+	std::map<int, bool> creDir; // <creatureID, if false reverse creature's animation> //TODO: move it to battle callback
 	ui8 animCount;
-	const CStack * activeStack; //number of active stack; nullptr - no one
-	const CStack * mouseHoveredStack; // stack below mouse pointer, used for border animation
-	const CStack * stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; nullptr of none
-	const CStack * selectedStack; //for Teleport / Sacrifice
+	const CStack *activeStack; //number of active stack; nullptr - no one
+	const CStack *mouseHoveredStack; // stack below mouse pointer, used for border animation
+	const CStack *stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; nullptr of none
+	const CStack *selectedStack; //for Teleport / Sacrifice
 	void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all
 	std::vector<BattleHex> occupyableHexes, //hexes available for active stack
 		attackableHexes; //hexes attackable by active stack
-    bool stackCountOutsideHexes[GameConstants::BFIELD_SIZE]; // hexes that when in front of a unit cause it's amount box to move back
+	bool stackCountOutsideHexes[GameConstants::BFIELD_SIZE]; // hexes that when in front of a unit cause it's amount box to move back
 	BattleHex previouslyHoveredHex; //number of hex that was hovered by the cursor a while ago
 	BattleHex currentlyHoveredHex; //number of hex that is supposed to be hovered (for a while it may be inappropriately set, but will be renewed soon)
 	int attackingHex; //hex from which the stack would perform attack with current cursor
 
-	shared_ptr<CPlayerInterface> tacticianInterface; //used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
+	std::shared_ptr<CPlayerInterface> tacticianInterface; //used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
 	bool tacticsMode;
-	bool stackCanCastSpell; //if true, active stack could possibly cats some target spell
+	bool stackCanCastSpell; //if true, active stack could possibly cast some target spell
 	bool creatureCasting; //if true, stack currently aims to cats a spell
 	bool spellDestSelectMode; //if true, player is choosing destination for his spell - only for GUI / console
-	PossibleActions spellSelMode;
-	BattleAction * spellToCast; //spell for which player is choosing destination
-	const CSpell * sp; //spell pointer for convenience
+	BattleAction *spellToCast; //spell for which player is choosing destination
+	const CSpell *sp; //spell pointer for convenience
 	si32 creatureSpellToCast;
 	std::vector<PossibleActions> possibleActions; //all actions possible to call at the moment by player
 	std::vector<PossibleActions> localActions; //actions possible to take on hovered hex
@@ -169,15 +169,18 @@ private:
 	PossibleActions selectedAction; //last action chosen (and saved) by player
 	PossibleActions illegalAction; //most likely action that can't be performed here
 
-	void setActiveStack(const CStack * stack);
-	void setHoveredStack(const CStack * stack);
+	void setActiveStack(const CStack *stack);
+	void setHoveredStack(const CStack *stack);
 
 	void requestAutofightingAIToTakeAction();
 
-	void getPossibleActionsForStack (const CStack * stack); //called when stack gets its turn
+	void getPossibleActionsForStack (const CStack *stack, const bool forceCast); //called when stack gets its turn
 	void endCastingSpell(); //ends casting spell (eg. when spell has been cast or canceled)
 
-	void printConsoleAttacked(const CStack * defender, int dmg, int killed, const CStack * attacker, bool Multiple);
+	//force active stack to cast a spell if possible
+	void enterCreatureCastingMode();
+
+	void printConsoleAttacked(const CStack *defender, int dmg, int killed, const CStack *attacker, bool Multiple);
 
 	std::list<ProjectileInfo> projectiles; //projectiles flying on battlefield
 	void giveCommand(Battle::ActionType action, BattleHex tile, ui32 stackID, si32 additional=-1, si32 selectedStack = -1);
@@ -191,66 +194,83 @@ private:
 	{
 	private:
 		SDL_Surface* walls[18];
-		const CBattleInterface * owner;
+		const CBattleInterface *owner;
 	public:
-		const CGTownInstance * town; //besieged town
-		
-		SiegeHelper(const CGTownInstance * siegeTown, const CBattleInterface * _owner); //c-tor
+		const CGTownInstance *town; //besieged town
+
+		SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface *_owner); //c-tor
 		~SiegeHelper(); //d-tor
 
-		//filename getters
-		//what: 0 - background,  1 - background wall,  2 - keep,          3 - bottom tower,  4 - bottom wall,
-		//      5 - below gate,  6 - over gate,        7 - upper wall,    8 - upper tower,   9 - gate,
-		//      10 - gate arch, 11 - bottom static    12 - upper static, 13 - moat,         14 - moat background,
-		//      15 - keep battlement, 16 - bottom battlement, 17 - upper battlement;
-		//      state uses EWallState enum
 		std::string getSiegeName(ui16 what) const;
-		std::string getSiegeName(ui16 what, int state) const;
-
-		void printPartOfWall(SDL_Surface * to, int what);//what: 1 - background wall, 2 - keep, 3 - bottom tower, 4 - bottom wall, 5 - below gate, 6 - over gate, 7 - upper wall, 8 - uppert tower, 9 - gate, 10 - gate arch, 11 - bottom static wall, 12 - upper static wall, 15 - keep creature cover, 16 - bottom turret creature cover, 17 - upper turret creature cover
+		std::string getSiegeName(ui16 what, int state) const; // state uses EWallState enum
+
+		void printPartOfWall(SDL_Surface *to, int what);
+
+		enum EWallVisual
+		{
+			BACKGROUND = 0,
+			BACKGROUND_WALL = 1,
+			KEEP,
+			BOTTOM_TOWER,
+			BOTTOM_WALL,
+			WALL_BELLOW_GATE,
+			WALL_OVER_GATE,
+			UPPER_WALL,
+			UPPER_TOWER,
+			GATE,
+			GATE_ARCH,
+			BOTTOM_STATIC_WALL,
+			UPPER_STATIC_WALL,
+			MOAT,
+			BACKGROUND_MOAT,
+			KEEP_BATTLEMENT,
+			BOTTOM_BATTLEMENT,
+			UPPER_BATTLEMENT
+		};
 
 		friend class CBattleInterface;
-	} * siegeH;
+	} *siegeH;
 
-	shared_ptr<CPlayerInterface> attackerInt, defenderInt; //because LOCPLINT is not enough in hotSeat
-	shared_ptr<CPlayerInterface> curInt; //current player interface
-	const CGHeroInstance * getActiveHero(); //returns hero that can currently cast a spell
+	std::shared_ptr<CPlayerInterface> attackerInt, defenderInt; //because LOCPLINT is not enough in hotSeat
+	std::shared_ptr<CPlayerInterface> curInt; //current player interface
+	const CGHeroInstance *getActiveHero(); //returns hero that can currently cast a spell
 
 	/** Methods for displaying battle screen */
-	void showBackground(SDL_Surface * to);
+	void showBackground(SDL_Surface *to);
 
-	void showBackgroundImage(SDL_Surface * to);
-	void showAbsoluteObstacles(SDL_Surface * to);
-	void showHighlightedHexes(SDL_Surface * to);
-	void showHighlightedHex(SDL_Surface * to, BattleHex hex);
-	void showInterface(SDL_Surface * to);
+	void showBackgroundImage(SDL_Surface *to);
+	void showAbsoluteObstacles(SDL_Surface *to);
+	void showHighlightedHexes(SDL_Surface *to);
+	void showHighlightedHex(SDL_Surface *to, BattleHex hex);
+	void showInterface(SDL_Surface *to);
 
-	void showBattlefieldObjects(SDL_Surface * to);
+	void showBattlefieldObjects(SDL_Surface *to);
 
-	void showAliveStacks(SDL_Surface * to, std::vector<const CStack *> stacks);
-	void showStacks(SDL_Surface * to, std::vector<const CStack *> stacks);
-	void showObstacles(SDL_Surface *to, std::vector<shared_ptr<const CObstacleInstance> > &obstacles);
-	void showPiecesOfWall(SDL_Surface * to, std::vector<int> pieces);
+	void showAliveStacks(SDL_Surface *to, std::vector<const CStack *> stacks);
+	void showStacks(SDL_Surface *to, std::vector<const CStack *> stacks);
+	void showObstacles(SDL_Surface *to, std::vector<std::shared_ptr<const CObstacleInstance>> &obstacles);
+	void showPiecesOfWall(SDL_Surface *to, std::vector<int> pieces);
 
 	void showBattleEffects(SDL_Surface *to, const std::vector<const BattleEffect *> &battleEffects);
-	void showProjectiles(SDL_Surface * to);
+	void showProjectiles(SDL_Surface *to);
 
 	BattleObjectsByHex sortObjectsByHex();
 	void updateBattleAnimations();
 
 	SDL_Surface *getObstacleImage(const CObstacleInstance &oi);
 	Point getObstaclePosition(SDL_Surface *image, const CObstacleInstance &obstacle);
-	void redrawBackgroundWithHexes(const CStack * activeStack);
+	void redrawBackgroundWithHexes(const CStack *activeStack);
 	/** End of battle screen blitting methods */
 
+	PossibleActions getCasterAction(const CSpell *spell, const ISpellCaster *caster, ECastingMode::ECastingMode mode) const;
 public:
-	std::list<std::pair<CBattleAnimation *, bool> > pendingAnims; //currently displayed animations <anim, initialized>
-	void addNewAnim(CBattleAnimation * anim); //adds new anim to pendingAnims
+	std::list<std::pair<CBattleAnimation *, bool>> pendingAnims; //currently displayed animations <anim, initialized>
+	void addNewAnim(CBattleAnimation *anim); //adds new anim to pendingAnims
 	ui32 animIDhelper; //for giving IDs for animations
 	static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
 
-	CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen); //c-tor
-	~CBattleInterface(); //d-tor
+	CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen); //c-tor
+	virtual ~CBattleInterface(); //d-tor
 
 	//std::vector<TimeInterested*> timeinterested; //animation handling
 	void setPrintCellBorders(bool set); //if true, cell borders will be printed
@@ -258,18 +278,18 @@ public:
 	void setPrintMouseShadow(bool set); //if true, hex under mouse will be shaded
 	void setAnimSpeed(int set); //speed of animation; range 1..100
 	int getAnimSpeed() const; //speed of animation; range 1..100
-	CPlayerInterface * getCurrentPlayerInterface() const;
+	CPlayerInterface *getCurrentPlayerInterface() const;
 
 	std::vector<CClickableHex*> bfield; //11 lines, 17 hexes on each
-	SDL_Surface * cellBorder, * cellShade;
+	SDL_Surface *cellBorder, *cellShade;
 	CondSh<BattleAction *> *givenCommand; //data != nullptr if we have i.e. moved current unit
 	bool myTurn; //if true, interface is active (commands can be ordered)
-	CBattleResultWindow * resWindow; //window of end of battle
+	CBattleResultWindow *resWindow; //window of end of battle
 
 	bool moveStarted; //if true, the creature that is already moving is going to make its first step
 	int moveSoundHander; // sound handler used when moving a unit
 
-	const BattleResult * bresult; //result of a battle; if non-zero then display when all animations end
+	const BattleResult *bresult; //result of a battle; if non-zero then display when all animations end
 
 	// block all UI elements, e.g. during enemy turn
 	// unlike activate/deactivate this method will correctly grey-out all elements
@@ -291,59 +311,65 @@ public:
 	void bEndTacticPhase();
 	//end of button handle funcs
 	//napisz tu klase odpowiadajaca za wyswietlanie bitwy i obsluge uzytkownika, polecenia ma przekazywac callbackiem
-	void activate();
-	void deactivate();
-	void keyPressed(const SDL_KeyboardEvent & key);
-	void mouseMoved(const SDL_MouseMotionEvent &sEvent);
-	void clickRight(tribool down, bool previousState);
+	void activate() override;
+	void deactivate() override;
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	void mouseMoved(const SDL_MouseMotionEvent &sEvent) override;
+	void clickRight(tribool down, bool previousState) override;
 
-	void show(SDL_Surface *to);
-	void showAll(SDL_Surface *to);
+	void show(SDL_Surface *to) override;
+	void showAll(SDL_Surface *to) override;
 
 	//call-ins
 	void startAction(const BattleAction* action);
-	void newStack(const CStack * stack); //new stack appeared on battlefield
+	void newStack(const CStack *stack); //new stack appeared on battlefield
 	void stackRemoved(int stackID); //stack disappeared from batlefiled
-	void stackActivated(const CStack * stack); //active stack has been changed
-	void stackMoved(const CStack * stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
+	void stackActivated(const CStack *stack); //active stack has been changed
+	void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
 	void waitForAnims();
 	void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
-	void stackAttacking(const CStack * attacker, BattleHex dest, const CStack * attacked, bool shooting); //called when stack with id ID is attacking something on hex dest
+	void stackAttacking(const CStack *attacker, BattleHex dest, const CStack *attacked, bool shooting); //called when stack with id ID is attacking something on hex dest
 	void newRoundFirst( int round );
 	void newRound(int number); //caled when round is ended; number is the number of round
 	void hexLclicked(int whichOne); //hex only call-in
 	void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls
 	void battleFinished(const BattleResult& br); //called when battle is finished - battleresult window should be printed
 	void displayBattleFinished(); //displays battle result
-	void spellCast(const BattleSpellCast * sc); //called when a hero casts a spell
+	void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
 	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
 	void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
-	void displayEffect(ui32 effect, int destTile, bool areaEffect = true); //displays custom effect on the battlefield
-	void displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
-	void displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
-	
+	void displayEffect(ui32 effect, int destTile); //displays custom effect on the battlefield
+
+	void displaySpellCast(SpellID spellID, BattleHex destinationTile); //displays spell`s cast animation
+	void displaySpellEffect(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation
+	void displaySpellHit(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation
+
+	void displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile);
+
 	void battleTriggerEffect(const BattleTriggerEffect & bte);
 	void setBattleCursor(const int myNumber); //really complex and messy, sets attackingHex
 	void endAction(const BattleAction* action);
 	void hideQueue();
 	void showQueue();
-	PossibleActions selectionTypeByPositiveness(const CSpell & spell);
+
 	Rect hexPosition(BattleHex hex) const;
 
 	void handleHex(BattleHex myNumber, int eventType);
-	bool isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber);
-	bool canStackMoveHere (const CStack * sactive, BattleHex MyNumber); //TODO: move to BattleState / callback
+	bool isCastingPossibleHere (const CStack *sactive, const CStack *shere, BattleHex myNumber);
+	bool canStackMoveHere (const CStack *sactive, BattleHex MyNumber); //TODO: move to BattleState / callback
 
 	BattleHex fromWhichHexAttack(BattleHex myNumber);
 	void obstaclePlaced(const CObstacleInstance & oi);
 
-	const CGHeroInstance * currentHero() const;
+	void gateStateChanged(const EGateState state);
+
+	const CGHeroInstance *currentHero() const;
 	InfoAboutHero enemyHero() const;
 
 	friend class CPlayerInterface;
 	friend class CButton;
 	friend class CInGameConsole;
-	
+
 	friend class CBattleResultWindow;
 	friend class CBattleHero;
 	friend class CSpellEffectAnimation;

+ 77 - 7
client/battle/CBattleInterfaceClasses.cpp

@@ -29,6 +29,7 @@
 #include "../../lib/NetPacks.h"
 #include "../../lib/StartInfo.h"
 #include "../../lib/CondSh.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 
 /*
  * CBattleInterfaceClasses.cpp, part of VCMI engine
@@ -69,6 +70,7 @@ void CBattleConsole::showAll(SDL_Surface * to)
 
 bool CBattleConsole::addText(const std::string & text)
 {
+	logGlobal->traceStream() <<"CBattleConsole message: "<<text;
 	if(text.size()>70)
 		return false; //text too long!
 	int firstInToken = 0;
@@ -175,6 +177,15 @@ void CBattleHero::setPhase(int newPhase)
 	nextPhase = 0;
 }
 
+void CBattleHero::hover(bool on)
+{
+	//TODO: Make lines below work properly
+	if (on)
+		CCS->curh->changeGraphic(ECursor::COMBAT, 5);
+	else
+		CCS->curh->changeGraphic(ECursor::COMBAT, 0);
+}
+
 void CBattleHero::clickLeft(tribool down, bool previousState)
 {
 	if(myOwner->spellDestSelectMode) //we are casting a spell
@@ -189,8 +200,26 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
 		}
 		CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
 
-		auto  spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), myHero, myOwner->getCurrentPlayerInterface());
-		GH.pushInt(spellWindow);
+		GH.pushInt(new CSpellWindow(myHero, myOwner->getCurrentPlayerInterface()));
+	}
+}
+
+void CBattleHero::clickRight(tribool down, bool previousState)
+{
+	Point windowPosition;
+	windowPosition.x = (!flip) ? myOwner->pos.topLeft().x + 1 : myOwner->pos.topRight().x - 79;
+	windowPosition.y = myOwner->pos.y + 135;
+
+	InfoAboutHero targetHero;
+
+	if (down && myOwner->myTurn)
+	{
+		if (myHero != nullptr)
+			targetHero.initFromHero(myHero, InfoAboutHero::EInfoLevel::INBATTLE);
+		else
+			targetHero = myOwner->enemyHero();
+
+		GH.pushInt(new CHeroInfoWindow(targetHero, &windowPosition));
 	}
 }
 
@@ -245,7 +274,7 @@ CBattleHero::CBattleHero(const std::string & defName, bool flipG, PlayerColor pl
 		CSDL_Ext::alphaTransform(elem.bitmap);
 		graphics->blueToPlayersAdv(elem.bitmap, player);
 	}
-	addUsedEvents(LCLICK);
+	addUsedEvents(LCLICK | RCLICK | HOVER);
 
 	switchToNextPhase();
 }
@@ -350,7 +379,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 		auto heroInfo = owner.cb->battleGetHeroInfo(i);
 		const int xs[] = {21, 392};
 
-		if(heroInfo.portrait >= 0) //attacking hero 
+		if(heroInfo.portrait >= 0) //attacking hero
 		{
 			new CAnimImage("PortraitsLarge", heroInfo.portrait, 0, xs[i], 38);
 			sideNames[i] = heroInfo.name;
@@ -418,7 +447,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 			boost::algorithm::replace_first(str,"%s",ourHero->name);
 			boost::algorithm::replace_first(str,"%d",boost::lexical_cast<std::string>(br.exp[weAreAttacker?0:1]));
 		}
-		
+
 		new CTextBox(str, Rect(69, 203, 330, 68), 0, FONT_SMALL, CENTER, Colors::WHITE);
 	}
 	else // we lose
@@ -479,7 +508,7 @@ void CBattleResultWindow::bExitf()
 	if(dynamic_cast<CBattleInterface*>(GH.topInt()))
 		GH.popInts(1); //pop battle interface if present
 
-	//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle, 
+	//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle,
 	//so we can be sure that there is no dialogs left on GUI stack.
 	intTmp.showingDialog->setn(false);
 	CCS->videoh->close();
@@ -502,7 +531,7 @@ Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBatt
 			break;
 		case -4: //upper turret
 			ret = cbi->siegeH->town->town->clientInfo.siegePositions[20];
-			break;	
+			break;
 		}
 	}
 	else
@@ -612,6 +641,47 @@ void CClickableHex::clickRight(tribool down, bool previousState)
 	}
 }
 
+CHeroInfoWindow::CHeroInfoWindow(const InfoAboutHero &hero, Point *position) : CWindowObject(RCLICK_POPUP | SHADOW_DISABLED, "CHRPOP")
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	if (position != nullptr)
+		moveTo(*position);
+	background->colorize(hero.owner); //maybe add this functionality to base class?
+
+	int attack = hero.details->primskills[0];
+	int defense = hero.details->primskills[1];
+	int power = hero.details->primskills[2];
+	int knowledge = hero.details->primskills[3];
+	int morale = hero.details->morale;
+	int luck = hero.details->luck;
+	int currentSpellPoints = hero.details->mana;
+	int maxSpellPoints = hero.details->manaLimit;
+
+	new CAnimImage("PortraitsLarge", hero.portrait, 0, 10, 6);
+
+	//primary stats
+	new CLabel(9, 75, EFonts::FONT_TINY, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[380] + ":");
+	new CLabel(9, 87, EFonts::FONT_TINY, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[381] + ":");
+	new CLabel(9, 99, EFonts::FONT_TINY, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[382] + ":");
+	new CLabel(9, 111, EFonts::FONT_TINY, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[383] + ":");
+
+	new CLabel(69, 87, EFonts::FONT_TINY, EAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(attack));
+	new CLabel(69, 99, EFonts::FONT_TINY, EAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(defense));
+	new CLabel(69, 111, EFonts::FONT_TINY, EAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(power));
+	new CLabel(69, 123, EFonts::FONT_TINY, EAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(knowledge));
+
+	//morale+luck
+	new CLabel(9, 131, EFonts::FONT_TINY, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[384] + ":");
+	new CLabel(9, 143, EFonts::FONT_TINY, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[385] + ":");
+
+	new CAnimImage("IMRL22", morale + 3, 0, 47, 131);
+	new CAnimImage("ILCK22", luck + 3, 0, 47, 143);
+
+	//spell points
+	new CLabel(39, 174, EFonts::FONT_TINY, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[387]);
+	new CLabel(39, 186, EFonts::FONT_TINY, EAlignment::CENTER, Colors::WHITE, std::to_string(currentSpellPoints) + "/" + std::to_string(maxSpellPoints));
+}
+
 void CStackQueue::update()
 {
 	stacksSorted.clear();

+ 20 - 11
client/battle/CBattleInterfaceClasses.h

@@ -2,6 +2,7 @@
 
 #include "../gui/CIntObject.h"
 #include "../../lib/BattleHex.h"
+#include "../windows/CWindowObject.h"
 
 struct SDL_Surface;
 class CDefHandler;
@@ -38,7 +39,7 @@ public:
 	std::string ingcAlter; //alternative text set by in-game console - very important!
 	int whoSetAlter; //who set alter text; 0 - battle interface or none, 1 - button
 	CBattleConsole();
-	void showAll(SDL_Surface * to = 0);
+	void showAll(SDL_Surface * to = 0) override;
 	bool addText(const std::string &text); //adds text at the last position; returns false if failed (e.g. text longer than 70 characters)
 	void alterText(const std::string &text); //place string at alterTxt
 	void eraseText(ui32 pos); //erases added text at position pos
@@ -60,13 +61,21 @@ public:
 	int nextPhase; //stage of animation to be set after current phase is fully displayed
 	int currentFrame, firstFrame, lastFrame; //frame of animation
 	ui8 flagAnim, animCount; //for flag animation
-	void show(SDL_Surface * to); //prints next frame of animation to to
+	void show(SDL_Surface * to) override; //prints next frame of animation to to
 	void setPhase(int newPhase); //sets phase of hero animation
-	void clickLeft(tribool down, bool previousState); //call-in
+	void hover(bool on) override;
+	void clickLeft(tribool down, bool previousState) override; //call-in
+	void clickRight(tribool down, bool previousState) override; //call-in
 	CBattleHero(const std::string &defName, bool filpG, PlayerColor player, const CGHeroInstance *hero, const CBattleInterface *owner); //c-tor
 	~CBattleHero(); //d-tor
 };
 
+class CHeroInfoWindow : public CWindowObject
+{
+public:
+	CHeroInfoWindow(const InfoAboutHero &hero, Point *position);
+};
+
 /// Class which manages the battle options window
 class CBattleOptionsWindow : public CIntObject
 {
@@ -96,8 +105,8 @@ public:
 
 	void bExitf(); //exit button callback
 
-	void activate();
-	void show(SDL_Surface * to = 0);
+	void activate() override;
+	void show(SDL_Surface * to = 0) override;
 };
 
 /// Class which stands for a single hex field on a battlefield
@@ -114,10 +123,10 @@ public:
 	static Point getXYUnitAnim(BattleHex hexNum, const CStack * creature, CBattleInterface * cbi); //returns (x, y) of left top corner of animation
 
 	//for user interactions
-	void hover (bool on);
-	void mouseMoved (const SDL_MouseMotionEvent &sEvent);
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
+	void hover (bool on) override;
+	void mouseMoved (const SDL_MouseMotionEvent &sEvent) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
 	CClickableHex();
 };
 
@@ -132,7 +141,7 @@ class CStackQueue : public CIntObject
 		const CStack *stack;
 		bool small;
 
-		void showAll(SDL_Surface * to);
+		void showAll(SDL_Surface * to) override;
 		void setStack(const CStack *nStack);
 		StackBox(bool small);
 	};
@@ -150,6 +159,6 @@ public:
 	CStackQueue(bool Embedded, CBattleInterface * _owner);
 	~CStackQueue();
 	void update();
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 	void blitBg(SDL_Surface * to);
 };

+ 8 - 23
client/battle/CCreatureAnimation.cpp

@@ -8,7 +8,6 @@
 #include "../../lib/filesystem/CBinaryReader.h"
 #include "../../lib/filesystem/CMemoryStream.h"
 
-#include "../gui/SDL_Extensions.h"
 #include "../gui/SDL_Pixels.h"
 
 /*
@@ -164,7 +163,9 @@ CCreatureAnimation::CCreatureAnimation(std::string name, TSpeedController contro
 		pixelDataSize = data.second;
 	}
 
-	CBinaryReader reader(new CMemoryStream(pixelData.get(), pixelDataSize));
+	CMemoryStream stm(pixelData.get(), pixelDataSize);
+
+	CBinaryReader reader(&stm);
 
 	reader.readInt32(); // def type, unused
 
@@ -178,7 +179,7 @@ CCreatureAnimation::CCreatureAnimation(std::string name, TSpeedController contro
 		elem.r = reader.readUInt8();
 		elem.g = reader.readUInt8();
 		elem.b = reader.readUInt8();
-		CSDL_Ext::colorSetAlpha(elem,0);
+		elem.a = SDL_ALPHA_OPAQUE;
 	}
 
 	for (int i=0; i<totalBlocks; i++)
@@ -267,11 +268,7 @@ static SDL_Color genShadow(ui8 alpha)
 
 static SDL_Color genBorderColor(ui8 alpha, const SDL_Color & base)
 {
-	#ifdef VCMI_SDL1
-	return CSDL_Ext::makeColor(base.r, base.g, base.b, ui8(base.unused * alpha / 256));
-	#else
 	return CSDL_Ext::makeColor(base.r, base.g, base.b, ui8(base.a * alpha / 256));
-	#endif
 }
 
 static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2)
@@ -281,22 +278,12 @@ static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2)
 
 static SDL_Color addColors(const SDL_Color & base, const SDL_Color & over)
 {
-	#ifdef VCMI_SDL1
-	return CSDL_Ext::makeColor(
-			mixChannels(over.r, base.r, over.unused, base.unused),
-			mixChannels(over.g, base.g, over.unused, base.unused),
-			mixChannels(over.b, base.b, over.unused, base.unused),
-			ui8(over.unused + base.unused * (255 - over.unused) / 256)
-			);
-	#else
 	return CSDL_Ext::makeColor(
 			mixChannels(over.r, base.r, over.a, base.a),
 			mixChannels(over.g, base.g, over.a, base.a),
 			mixChannels(over.b, base.b, over.a, base.a),
 			ui8(over.a + base.a * (255 - over.a) / 256)
 			);
-
-	#endif // VCMI_SDL1
 }
 
 std::array<SDL_Color, 8> CCreatureAnimation::genSpecialPalette()
@@ -322,7 +309,9 @@ void CCreatureAnimation::nextFrameT(SDL_Surface * dest, bool rotate)
 
 	ui32 offset = dataOffsets.at(type).at(floor(currentFrame));
 
-	CBinaryReader reader(new CMemoryStream(pixelData.get(), pixelDataSize));
+	CMemoryStream stm(pixelData.get(), pixelDataSize);
+
+	CBinaryReader reader(&stm);
 
 	reader.getStream()->seek(offset);
 
@@ -397,7 +386,7 @@ void CCreatureAnimation::nextFrame(SDL_Surface *dest, bool attacker)
 	case 3: return nextFrameT<3>(dest, !attacker);
 	case 4: return nextFrameT<4>(dest, !attacker);
 	default:
-        logGlobal->errorStream() << (int)dest->format->BitsPerPixel << " bpp is not supported!!!";
+		logGlobal->errorStream() << (int)dest->format->BitsPerPixel << " bpp is not supported!!!";
 	}
 }
 
@@ -427,11 +416,7 @@ inline void CCreatureAnimation::putPixel(ui8 * dest, const SDL_Color & color, si
 	if (index < 8)
 	{
 		const SDL_Color & pal = special[index];
-		#ifdef VCMI_SDL1
-		ColorPutter<bpp, 0>::PutColor(dest, pal.r, pal.g, pal.b, pal.unused);
-		#else
 		ColorPutter<bpp, 0>::PutColor(dest, pal.r, pal.g, pal.b, pal.a);
-		#endif // 0		
 	}
 	else
 	{

+ 1 - 1
client/battle/CCreatureAnimation.h

@@ -66,7 +66,7 @@ private:
 
 	//animation raw data
 	//TODO: use vector instead?
-	unique_ptr<ui8[]> pixelData;
+	std::unique_ptr<ui8[]> pixelData;
 	size_t pixelDataSize;
 
 	// speed of animation, measure in frames per second

+ 95 - 129
client/gui/CAnimation.cpp

@@ -51,11 +51,11 @@ class CompImageLoader
 	ui8 *position;
 	ui8 *entry;
 	ui32 currentLine;
-	
+
 	inline ui8 typeOf(ui8 color);
 	inline void NewEntry(ui8 color, size_t size);
 	inline void NewEntry(const ui8 * &data, size_t size);
-	
+
 public:
 	//load size raw pixels from data
 	inline void Load(size_t size, const ui8 * data);
@@ -75,29 +75,26 @@ class CFileCache
 	static const int cacheSize = 50; //Max number of cached files
 	struct FileData
 	{
-		ResourceID name;
-		size_t size;
-		ui8 * data;
+		ResourceID             name;
+		size_t                 size;
+		std::unique_ptr<ui8[]> data;
 
-		ui8 * getCopy()
+		std::unique_ptr<ui8[]> getCopy()
 		{
-			auto   ret = new ui8[size];
-			std::copy(data, data + size, ret);
+			auto ret = std::unique_ptr<ui8[]>(new ui8[size]);
+			std::copy(data.get(), data.get() + size, ret.get());
 			return ret;
 		}
-		FileData():
-		    size(0),
-		    data(nullptr)
+		FileData(ResourceID name_, size_t size_, std::unique_ptr<ui8[]> data_):
+			name{std::move(name_)},
+			size{size_},
+			data{std::move(data_)}
 		{}
-		~FileData()
-		{
-			delete [] data;
-		}
 	};
 
-	std::list<FileData> cache;
+	std::deque<FileData> cache;
 public:
-	ui8 * getCachedFile(ResourceID && rid)
+	std::unique_ptr<ui8[]> getCachedFile(ResourceID rid)
 	{
 		for(auto & file : cache)
 		{
@@ -107,12 +104,10 @@ public:
 		// Still here? Cache miss
 		if (cache.size() > cacheSize)
 			cache.pop_front();
-		cache.push_back(FileData());
 
 		auto data =  CResourceHandler::get()->load(rid)->readAll();
-		cache.back().name = ResourceID(rid);
-		cache.back().size = data.second;
-		cache.back().data = data.first.release();
+
+		cache.emplace_back(std::move(rid), data.second, std::move(data.first));
 
 		return cache.back().getCopy();
 	}
@@ -132,8 +127,8 @@ CDefFile::CDefFile(std::string Name):
 	static SDL_Color H3Palette[8] =
 	{
 		{   0,   0,   0,   0},// 100% - transparency
-		{   0,   0,   0,  64},//  75% - shadow border,
-		{   0,   0,   0, 128},// TODO: find exact value
+		{   0,   0,   0,  32},//  75% - shadow border,
+		{   0,   0,   0,  64},// TODO: find exact value
 		{   0,   0,   0, 128},// TODO: for transparency
 		{   0,   0,   0, 128},//  50% - shadow body
 		{   0,   0,   0,   0},// 100% - selection highlight
@@ -142,15 +137,15 @@ CDefFile::CDefFile(std::string Name):
 	};
 	data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION));
 
-	palette = new SDL_Color[256];
+	palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]);
 	int it = 0;
 
-	ui32 type = read_le_u32(data + it);
+	ui32 type = read_le_u32(data.get() + it);
 	it+=4;
 	//int width  = read_le_u32(data + it); it+=4;//not used
 	//int height = read_le_u32(data + it); it+=4;
 	it+=8;
-	ui32 totalBlocks = read_le_u32(data + it);
+	ui32 totalBlocks = read_le_u32(data.get() + it);
 	it+=4;
 
 	for (ui32 i= 0; i<256; i++)
@@ -158,18 +153,18 @@ CDefFile::CDefFile(std::string Name):
 		palette[i].r = data[it++];
 		palette[i].g = data[it++];
 		palette[i].b = data[it++];
-		CSDL_Ext::colorSetAlpha(palette[i],255);	
+		palette[i].a = SDL_ALPHA_OPAQUE;
 	}
 	if (type == 71 || type == 64)//Buttons/buildings don't have shadows\semi-transparency
-		memset(palette, 0, sizeof(SDL_Color)*2);
+		memset(palette.get(), 0, sizeof(SDL_Color)*2);
 	else
-		memcpy(palette, H3Palette, sizeof(SDL_Color)*8);//initialize shadow\selection colors
+		memcpy(palette.get(), H3Palette, sizeof(SDL_Color)*8);//initialize shadow\selection colors
 
 	for (ui32 i=0; i<totalBlocks; i++)
 	{
-		size_t blockID = read_le_u32(data + it);
+		size_t blockID = read_le_u32(data.get() + it);
 		it+=4;
-		size_t totalEntries = read_le_u32(data + it);
+		size_t totalEntries = read_le_u32(data.get() + it);
 		it+=12;
 		//8 unknown bytes - skipping
 
@@ -178,7 +173,7 @@ CDefFile::CDefFile(std::string Name):
 
 		for (ui32 j=0; j<totalEntries; j++)
 		{
-			size_t currOffset = read_le_u32(data + it);
+			size_t currOffset = read_le_u32(data.get() + it);
 			offset[blockID].push_back(currOffset);
 			it += 4;
 		}
@@ -192,7 +187,7 @@ void CDefFile::loadFrame(size_t frame, size_t group, ImageLoader &loader) const
 	it = offset.find(group);
 	assert (it != offset.end());
 
-	const ui8 * FDef = data+it->second[frame];
+	const ui8 * FDef = data.get()+it->second[frame];
 
 	const SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef);
 	SSpriteDef sprite;
@@ -210,7 +205,7 @@ void CDefFile::loadFrame(size_t frame, size_t group, ImageLoader &loader) const
 
 	loader.init(Point(sprite.width, sprite.height),
 	            Point(sprite.leftMargin, sprite.topMargin),
-	            Point(sprite.fullWidth, sprite.fullHeight), palette);
+	            Point(sprite.fullWidth, sprite.fullHeight), palette.get());
 
 	switch (sprite.format)
 	{
@@ -274,7 +269,7 @@ void CDefFile::loadFrame(size_t frame, size_t group, ImageLoader &loader) const
 
 					if (code==7)//Raw data
 					{
-						loader.Load(length, FDef[currentOffset]);
+						loader.Load(length, FDef + currentOffset);
 						currentOffset += length;
 					}
 					else//RLE
@@ -316,16 +311,12 @@ void CDefFile::loadFrame(size_t frame, size_t group, ImageLoader &loader) const
 			break;
 		}
 	default:
-        logGlobal->errorStream()<<"Error: unsupported format of def file: "<<sprite.format;
+	logGlobal->errorStream()<<"Error: unsupported format of def file: "<<sprite.format;
 		break;
 	}
 };
 
-CDefFile::~CDefFile()
-{
-	delete[] data;
-	delete[] palette;
-}
+CDefFile::~CDefFile() = default;
 
 const std::map<size_t, size_t > CDefFile::getEntries() const
 {
@@ -355,7 +346,11 @@ void SDLImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_C
 	image->fullSize = FullSize;
 
 	//Prepare surface
-	SDL_SetColors(image->surf, pal, 0, 256);
+	SDL_Palette * p = SDL_AllocPalette(256);
+	SDL_SetPaletteColors(p, pal, 0, 256);
+	SDL_SetSurfacePalette(image->surf, p);
+	SDL_FreePalette(p);
+
 	SDL_LockSurface(image->surf);
 	lineStart = position = (ui8*)image->surf->pixels;
 }
@@ -387,19 +382,19 @@ inline void SDLImageLoader::EndLine()
 SDLImageLoader::~SDLImageLoader()
 {
 	SDL_UnlockSurface(image->surf);
-	SDL_SetColorKey(image->surf, SDL_SRCCOLORKEY, 0);
+	SDL_SetColorKey(image->surf, SDL_TRUE, 0);
 	//TODO: RLE if compressed and bpp>1
 }
 
 ////////////////////////////////////////////////////////////////////////////////
- 
+
 CompImageLoader::CompImageLoader(CompImage * Img):
 	image(Img),
 	position(nullptr),
 	entry(nullptr),
 	currentLine(0)
 {
-	
+
 }
 
 void CompImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal)
@@ -444,14 +439,10 @@ inline ui8 CompImageLoader::typeOf(ui8 color)
 {
 	if (color == 0)
 		return 0;
-	#ifdef VCMI_SDL1
-	if (image->palette[color].unused != 255)
-		return 1;
-	#else
+
 	if (image->palette[color].a != 255)
 		return 1;
-	#endif // 0
-		
+
 	return 2;
 }
 
@@ -489,7 +480,7 @@ inline void CompImageLoader::Load(size_t size, const ui8 * data)
 		ui8 type = typeOf(color);
 		ui8 color2;
 		ui8 type2;
-		
+
 		if (size > 1)
 		{
 			do
@@ -616,7 +607,7 @@ SDLImage::SDLImage(std::string filename, bool compressed):
 
 	if (surf == nullptr)
 	{
-        logGlobal->errorStream() << "Error: failed to load image "<<filename;
+		logGlobal->errorStream() << "Error: failed to load image "<<filename;
 		return;
 	}
 	else
@@ -628,22 +619,11 @@ SDLImage::SDLImage(std::string filename, bool compressed):
 	{
 		SDL_Surface *temp = surf;
 		// add RLE flag
-		#ifdef VCMI_SDL1
-		if (surf->format->palette)
-		{
-			const SDL_Color &c = temp->format->palette->colors[0];
-			SDL_SetColorKey(temp, (SDL_SRCCOLORKEY | SDL_RLEACCEL),
-				SDL_MapRGB(temp -> format, c.r, c.g, c.b));
-		}
-		else
-			SDL_SetColorKey(temp, SDL_RLEACCEL, 0);
-		#else
 		if (surf->format->palette)
 		{
 			CSDL_Ext::setColorKey(temp,temp->format->palette->colors[0]);
 		}
-		SDL_SetSurfaceRLE(temp, SDL_RLEACCEL);		
-		#endif		
+		SDL_SetSurfaceRLE(temp, SDL_RLEACCEL);
 
 		// convert surface to enable RLE
 		surf = SDL_ConvertSurface(temp, temp->format, temp->flags);
@@ -651,10 +631,11 @@ SDLImage::SDLImage(std::string filename, bool compressed):
 	}
 }
 
-void SDLImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 rotation) const
+void SDLImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 alpha) const
 {
 	if (!surf)
 		return;
+
 	Rect sourceRect(margins.x, margins.y, surf->w, surf->h);
 	//TODO: rotation and scaling
 	if (src)
@@ -664,7 +645,21 @@ void SDLImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 rotat
 	Rect destRect(posX, posY, surf->w, surf->h);
 	destRect += sourceRect.topLeft();
 	sourceRect -= margins;
-	CSDL_Ext::blitSurface(surf, &sourceRect, where, &destRect);
+
+	if(surf->format->BitsPerPixel == 8)
+	{
+		CSDL_Ext::blit8bppAlphaTo24bpp(surf, &sourceRect, where, &destRect);
+	}
+	else if(surf->format->Amask == 0)
+	{
+		SDL_BlitSurface(surf, &sourceRect, where, &destRect);
+	}
+	else
+	{
+		SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND);
+		SDL_BlitSurface(surf, &sourceRect, where, &destRect);
+		SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE);
+	}
 }
 
 void SDLImage::playerColored(PlayerColor player)
@@ -691,7 +686,7 @@ CompImage::CompImage(const CDefFile *data, size_t frame, size_t group):
 	surf(nullptr),
 	line(nullptr),
 	palette(nullptr)
-	
+
 {
 	CompImageLoader loader(this);
 	data->loadFrame(frame, group, loader);
@@ -749,7 +744,7 @@ void CompImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 alph
 
 		currX = 0;
 		ui8 bpp = where->format->BytesPerPixel;
-		
+
 		//Calculate position for blitting: pixels + Y + X
 		ui8* blitPos = (ui8*) where->pixels;
 		if (rotation & 4)
@@ -780,7 +775,7 @@ void CompImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 alph
 void CompImage::BlitBlockWithBpp(ui8 bpp, ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha, bool rotated) const
 {
 	assert(bpp>1 && bpp<5);
-	
+
 	if (rotated)
 		switch (bpp)
 		{
@@ -811,21 +806,13 @@ void CompImage::BlitBlock(ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha)
 			for (size_t i=0; i<size; i++)
 			{
 				SDL_Color col = palette[*(data++)];
-				#ifdef VCMI_SDL1
-				col.unused = (ui32)col.unused*alpha/255;
-				#else
 				col.a = (ui32)col.a*alpha/255;
-				#endif // 0				
 				ColorPutter<bpp, 1>::PutColorAlpha(dest, col);
 			}
 			return;
 		}
-		
-		#ifdef VCMI_SDL1
-		if (palette[color].unused == 255)
-		#else
+
 		if (palette[color].a == 255)
-		#endif // 0		
 		{
 			//Put row of RGB data
 			for (size_t i=0; i<size; i++)
@@ -836,25 +823,12 @@ void CompImage::BlitBlock(ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha)
 			//Put row of RGBA data
 			for (size_t i=0; i<size; i++)
 				ColorPutter<bpp, 1>::PutColorAlpha(dest, palette[*(data++)]);
-			
+
 		}
 	}
 	//RLE-d sequence
 	else
 	{
-		#ifdef VCMI_SDL1
-		if (alpha != 255 && palette[type].unused !=0)//Per-surface alpha is set
-		{
-			SDL_Color col = palette[type];
-			col.unused = (int)col.unused*(255-alpha)/255;
-			for (size_t i=0; i<size; i++)
-				ColorPutter<bpp, 1>::PutColorAlpha(dest, col);
-			return;
-		}	
-
-		switch (palette[type].unused)
-				
-		#else
 		if (alpha != 255 && palette[type].a !=0)//Per-surface alpha is set
 		{
 			SDL_Color col = palette[type];
@@ -863,9 +837,8 @@ void CompImage::BlitBlock(ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha)
 				ColorPutter<bpp, 1>::PutColorAlpha(dest, col);
 			return;
 		}
-		
+
 		switch (palette[type].a)
-		#endif // 0
 		{
 			case 0:
 			{
@@ -991,7 +964,7 @@ bool CAnimation::loadFrame(CDefFile * file, size_t frame, size_t group)
 	else //load from separate file
 	{
 		std::string filename = source[group][frame].Struct().find("file")->second.String();
-	
+
 		IImage * img = getFromExtraDef(filename);
 		if (!img)
 			img = new SDLImage(filename, compressed);
@@ -1092,13 +1065,14 @@ CDefFile * CAnimation::getFile() const
 
 void CAnimation::printError(size_t frame, size_t group, std::string type) const
 {
-    logGlobal->errorStream() << type <<" error: Request for frame not present in CAnimation! "
-          <<"\tFile name: "<<name<<" Group: "<<group<<" Frame: "<<frame;
+	logGlobal->errorStream() << type << " error: Request for frame not present in CAnimation! "
+		<< "\tFile name: " << name << " Group: " << group << " Frame: " << frame;
 }
 
 CAnimation::CAnimation(std::string Name, bool Compressed):
 	name(Name),
-	compressed(Compressed)
+	compressed(Compressed),
+	preloaded(false)
 {
 	size_t dotPos = name.find_last_of('.');
 	if ( dotPos!=-1 )
@@ -1107,27 +1081,28 @@ CAnimation::CAnimation(std::string Name, bool Compressed):
 	CDefFile * file = getFile();
 	init(file);
 	delete file;
-	loadedAnims.insert(this);
 }
 
 CAnimation::CAnimation():
 	name(""),
-	compressed(false)
+	compressed(false),
+	preloaded(false)
 {
 	init(nullptr);
-	loadedAnims.insert(this);
 }
 
 CAnimation::~CAnimation()
 {
-	if (!images.empty())
+	if(preloaded)
+		unload();
+
+	if(!images.empty())
 	{
-        logGlobal->warnStream()<<"Warning: not all frames were unloaded from "<<name;
+		logGlobal->warnStream()<<"Warning: not all frames were unloaded from "<<name;
 		for (auto & elem : images)
 			for (auto & _image : elem.second)
 				delete _image.second;
 	}
-	loadedAnims.erase(this);
 }
 
 void CAnimation::setCustom(std::string filename, size_t frame, size_t group)
@@ -1171,6 +1146,12 @@ void CAnimation::unload()
 
 }
 
+void CAnimation::preload()
+{
+	preloaded = true;
+	load();
+}
+
 void CAnimation::loadGroup(size_t group)
 {
 	CDefFile * file = getFile();
@@ -1209,21 +1190,6 @@ size_t CAnimation::size(size_t group) const
 	return 0;
 }
 
-std::set<CAnimation*> CAnimation::loadedAnims;
-
-void CAnimation::getAnimInfo()
-{
-    logGlobal->errorStream()<<"Animation stats: Loaded "<<loadedAnims.size()<<" total";
-	for (auto anim : loadedAnims)
-	{
-		
-        logGlobal->errorStream()<<"Name: "<<anim->name<<" Groups: "<<anim->images.size();
-		if (!anim->images.empty())
-            logGlobal->errorStream()<<", "<<anim->images.begin()->second.size()<<" image loaded in group "<< anim->images.begin()->first;
-	}
-}
-
-
 float CFadeAnimation::initialCounter() const
 {
 	if (fadingMode == EMode::OUT)
@@ -1235,12 +1201,12 @@ void CFadeAnimation::update()
 {
 	if (!fading)
 		return;
-	
+
 	if (fadingMode == EMode::OUT)
 		fadingCounter -= delta;
 	else
 		fadingCounter += delta;
-		
+
 	if (isFinished())
 	{
 		fading = false;
@@ -1269,7 +1235,7 @@ CFadeAnimation::CFadeAnimation()
 CFadeAnimation::~CFadeAnimation()
 {
 	if (fadingSurface && shouldFreeSurface)
-		SDL_FreeSurface(fadingSurface);		
+		SDL_FreeSurface(fadingSurface);
 }
 
 void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd /* = false */, float animDelta /* = DEFAULT_DELTA */)
@@ -1280,17 +1246,17 @@ void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurf
 		// (alternatively, we could just return here to ignore the new fade request until this one finished (but we'd need to free the passed bitmap to avoid leaks))
 		logGlobal->warnStream() << "Tried to init fading animation that is already running.";
 		if (fadingSurface && shouldFreeSurface)
-			SDL_FreeSurface(fadingSurface); 
-	}		
+			SDL_FreeSurface(fadingSurface);
+	}
 	if (animDelta <= 0.0f)
 	{
 		logGlobal->warnStream() << "Fade anim: delta should be positive; " << animDelta << " given.";
 		animDelta = DEFAULT_DELTA;
 	}
-	
+
 	if (sourceSurface)
 		fadingSurface = sourceSurface;
-	
+
 	delta = animDelta;
 	fadingMode = mode;
 	fadingCounter = initialCounter();
@@ -1299,13 +1265,13 @@ void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurf
 }
 
 void CFadeAnimation::draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRect, SDL_Rect * destRect)
-{	
+{
 	if (!fading || !fadingSurface || fadingMode == EMode::NONE)
 	{
 		fading = false;
 		return;
 	}
-	
+
 	CSDL_Ext::setAlpha(fadingSurface, fadingCounter * 255);
 	SDL_BlitSurface(fadingSurface, const_cast<SDL_Rect *>(sourceRect), targetSurface, destRect); //FIXME
 	CSDL_Ext::setAlpha(fadingSurface, 255);

+ 15 - 15
client/gui/CAnimation.h

@@ -39,8 +39,8 @@ private:
 	//offset[group][frame] - offset of frame data in file
 	std::map<size_t, std::vector <size_t> > offset;
 
-	ui8 * data;
-	SDL_Color * palette;
+	std::unique_ptr<ui8[]>       data;
+	std::unique_ptr<SDL_Color[]> palette;
 
 public:
 	CDefFile(std::string Name);
@@ -98,10 +98,11 @@ public:
 	SDLImage(SDL_Surface * from, bool extraRef);
 	~SDLImage();
 
-	void draw(SDL_Surface *where, int posX=0, int posY=0, Rect *src=nullptr,  ui8 alpha=255) const;
-	void playerColored(PlayerColor player);
-	int width() const;
-	int height() const;
+	void draw(SDL_Surface *where, int posX=0, int posY=0, Rect *src=nullptr,  ui8 alpha=255) const override;
+
+	void playerColored(PlayerColor player) override;
+	int width() const override;
+	int height() const override;
 
 	friend class SDLImageLoader;
 };
@@ -144,10 +145,10 @@ public:
 	CompImage(SDL_Surface * surf);
 	~CompImage();
 
-	void draw(SDL_Surface *where, int posX=0, int posY=0, Rect *src=nullptr, ui8 alpha=255) const;
-	void playerColored(PlayerColor player);
-	int width() const;
-	int height() const;
+	void draw(SDL_Surface *where, int posX=0, int posY=0, Rect *src=nullptr, ui8 alpha=255) const override;
+	void playerColored(PlayerColor player) override;
+	int width() const override;
+	int height() const override;
 
 	friend class CompImageLoader;
 };
@@ -169,6 +170,8 @@ private:
 	//if true all frames will be stored in compressed (RLE) state
 	const bool compressed;
 
+	bool preloaded;
+
 	//loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded
 	bool loadFrame(CDefFile * file, size_t frame, size_t group);
 
@@ -195,10 +198,6 @@ public:
 	CAnimation();
 	~CAnimation();
 
-    //static method for debugging - print info about loaded animations
-	static void getAnimInfo();
-	static std::set<CAnimation*> loadedAnims;
-
 	//add custom surface to the selected position.
 	void setCustom(std::string filename, size_t frame, size_t group=0);
 
@@ -208,6 +207,7 @@ public:
 	//all available frames
 	void load  ();
 	void unload();
+	void preload();
 
 	//all frames from group
 	void loadGroup  (size_t group);
@@ -236,7 +236,7 @@ private:
 	bool fading;
 	float fadingCounter;
 	bool shouldFreeSurface;
-	
+
 	float initialCounter() const;
 	bool isFinished() const;
 public:

+ 0 - 2
client/gui/CCursorHandler.cpp

@@ -27,10 +27,8 @@ void CCursorHandler::initCursor()
 	currentCursor = nullptr;
 
 	help = CSDL_Ext::newSurface(40,40);
-	#ifndef VCMI_SDL1
 	//No blending. Ensure, that we are copying pixels during "screen restore draw"
 	SDL_SetSurfaceBlendMode(help,SDL_BLENDMODE_NONE);	
-	#endif // VCMI_SDL1
 	SDL_ShowCursor(SDL_DISABLE);
 
 	changeGraphic(ECursor::ADVENTURE, 0);

+ 59 - 83
client/gui/CGuiHandler.cpp

@@ -1,5 +1,6 @@
 #include "StdInc.h"
 #include "CGuiHandler.h"
+#include "../lib/CondSh.h"
 
 #include <SDL.h>
 
@@ -15,9 +16,10 @@
 extern std::queue<SDL_Event> events;
 extern boost::mutex eventsM;
 
+CondSh<bool> CGuiHandler::terminate_cond;
 boost::thread_specific_ptr<bool> inGuiThread;
 
-SObjectConstruction::SObjectConstruction( CIntObject *obj )
+SObjectConstruction::SObjectConstruction(CIntObject *obj)
 :myObj(obj)
 {
 	GH.createdObj.push_front(obj);
@@ -63,10 +65,7 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
 	processList(CIntObject::TIME,activityFlag,&timeinterested,cb);
 	processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);
 	processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,cb);
-	
-	#ifndef VCMI_SDL1
 	processList(CIntObject::TEXTINPUT,activityFlag,&textInterested,cb);
-	#endif // VCMI_SDL1
 }
 
 void CGuiHandler::handleElementActivate(CIntObject * elem, ui16 activityFlag)
@@ -87,7 +86,7 @@ void CGuiHandler::handleElementDeActivate(CIntObject * elem, ui16 activityFlag)
 	elem->active_m &= ~activityFlag;
 }
 
-void CGuiHandler::popInt( IShowActivatable *top )
+void CGuiHandler::popInt(IShowActivatable *top)
 {
 	assert(listInt.front() == top);
 	top->deactivate();
@@ -98,7 +97,7 @@ void CGuiHandler::popInt( IShowActivatable *top )
 	totalRedraw();
 }
 
-void CGuiHandler::popIntTotally( IShowActivatable *top )
+void CGuiHandler::popIntTotally(IShowActivatable *top)
 {
 	assert(listInt.front() == top);
 	popInt(top);
@@ -106,7 +105,7 @@ void CGuiHandler::popIntTotally( IShowActivatable *top )
 	fakeMouseMove();
 }
 
-void CGuiHandler::pushInt( IShowActivatable *newInt )
+void CGuiHandler::pushInt(IShowActivatable *newInt)
 {
 	assert(newInt);
 	assert(boost::range::find(listInt, newInt) == listInt.end()); // do not add same object twice
@@ -122,7 +121,7 @@ void CGuiHandler::pushInt( IShowActivatable *newInt )
 	totalRedraw();
 }
 
-void CGuiHandler::popInts( int howMany )
+void CGuiHandler::popInts(int howMany)
 {
 	if(!howMany) return; //senseless but who knows...
 
@@ -196,10 +195,8 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 		//translate numpad keys
 		if(key.keysym.sym == SDLK_KP_ENTER)
 		{
-			key.keysym.sym = (SDLKey)SDLK_RETURN;
-			#ifndef VCMI_SDL1
+			key.keysym.sym = SDLK_RETURN;
 			key.keysym.scancode = SDL_SCANCODE_RETURN;
-			#endif // VCMI_SDL1
 		}
 
 		bool keysCaptured = false;
@@ -270,27 +267,18 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 				}
 			}
 		}
-		#ifdef VCMI_SDL1 //SDL1x only events
-		else if(sEvent->button.button == SDL_BUTTON_WHEELDOWN || sEvent->button.button == SDL_BUTTON_WHEELUP)
-		{
-			std::list<CIntObject*> hlp = wheelInterested;
-			for(auto i=hlp.begin(); i != hlp.end() && current; i++)
-			{
-				if(!vstd::contains(wheelInterested,*i)) continue;
-				(*i)->wheelScrolled(sEvent->button.button == SDL_BUTTON_WHEELDOWN, isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y));
-			}
-		}
-		#endif
 	}
-	#ifndef VCMI_SDL1 //SDL2x only events	
 	else if (sEvent->type == SDL_MOUSEWHEEL)
 	{
 		std::list<CIntObject*> hlp = wheelInterested;
 		for(auto i=hlp.begin(); i != hlp.end() && current; i++)
 		{
 			if(!vstd::contains(wheelInterested,*i)) continue;
-			(*i)->wheelScrolled(sEvent->wheel.y < 0, isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y));
-		}		
+			// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
+			int x = 0, y = 0;
+			SDL_GetMouseState(&x, &y);
+			(*i)->wheelScrolled(sEvent->wheel.y < 0, isItIn(&(*i)->pos, x, y));
+		}
 	}
 	else if(sEvent->type == SDL_TEXTINPUT)
 	{
@@ -307,7 +295,6 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 		}
 	}	
 	//todo: muiltitouch
-	#endif // VCMI_SDL1
 	else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_LEFT))
 	{
 		std::list<CIntObject*> hlp = lclickable;
@@ -341,7 +328,6 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 		}
 	}
 	current = nullptr;
-
 } //event end
 
 void CGuiHandler::handleMouseMotion(SDL_Event *sEvent)
@@ -378,7 +364,7 @@ void CGuiHandler::simpleRedraw()
 	objsToBlit.back()->show(screen); //blit active interface/window
 }
 
-void CGuiHandler::handleMoveInterested( const SDL_MouseMotionEvent & motion )
+void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
 {
 	//sending active, MotionInterested objects mouseMoved() call
 	std::list<CIntObject*> miCopy = motioninterested;
@@ -394,12 +380,9 @@ void CGuiHandler::handleMoveInterested( const SDL_MouseMotionEvent & motion )
 void CGuiHandler::fakeMouseMove()
 {
 	SDL_Event evnt;
-#ifdef VCMI_SDL1
-	SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0};
-#else
 	SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0};
-#endif	
 	int x, y;
+
 	sme.state = SDL_GetMouseState(&x, &y);
 	sme.x = x;
 	sme.y = y;
@@ -411,29 +394,37 @@ void CGuiHandler::fakeMouseMove()
 
 void CGuiHandler::renderFrame()
 {
-	auto doUpdate = [this]()
+
+	// Updating GUI requires locking pim mutex (that protects screen and GUI state).
+	// During game:
+	// When ending the game, the pim mutex might be hold by other thread,
+	// that will notify us about the ending game by setting terminate_cond flag.		
+	//in PreGame terminate_cond stay false 
+		
+	bool acquiredTheLockOnPim = false; //for tracking whether pim mutex locking succeeded
+	while(!terminate_cond.get() && !(acquiredTheLockOnPim = CPlayerInterface::pim->try_lock())) //try acquiring long until it succeeds or we are told to terminate
+		boost::this_thread::sleep(boost::posix_time::milliseconds(15));
+
+	if(acquiredTheLockOnPim)
 	{
+		// If we are here, pim mutex has been successfully locked - let's store it in a safe RAII lock.
+		boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim, boost::adopt_lock);
+
 		if(nullptr != curInt)
-		{
-			curInt -> update();
-		}			
+			curInt->update();
+		
+		if (settings["general"]["showfps"].Bool())
+			drawFPSCounter();		
+			
 		// draw the mouse cursor and update the screen
 		CCS->curh->render();
 
-		#ifndef	VCMI_SDL1
 		if(0 != SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr))
 			logGlobal->errorStream() << __FUNCTION__ << " SDL_RenderCopy " << SDL_GetError();
 
-		SDL_RenderPresent(mainRenderer);				
-		#endif		
-		
-	};
-	
-	if(curInt)
-		curInt->runLocked(doUpdate);
-	else
-		doUpdate();
-	
+		SDL_RenderPresent(mainRenderer);			
+	}					
+
 	mainFPSmng->framerateDelay(); // holds a constant FPS	
 }
 
@@ -448,6 +439,8 @@ CGuiHandler::CGuiHandler()
 	// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate
 	mainFPSmng = new CFramerateManager(48);
 	//do not init CFramerateManager here --AVS
+	
+	terminate_cond.set(false);
 }
 
 CGuiHandler::~CGuiHandler()
@@ -470,23 +463,8 @@ void CGuiHandler::drawFPSCounter()
 	graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, yellow, Point(10, 10));
 }
 
-SDLKey CGuiHandler::arrowToNum( SDLKey key )
+SDL_Keycode CGuiHandler::arrowToNum(SDL_Keycode key)
 {
-	#ifdef VCMI_SDL1
-	switch(key)
-	{
-	case SDLK_DOWN:
-		return SDLK_KP2;
-	case SDLK_UP:
-		return SDLK_KP8;
-	case SDLK_LEFT:
-		return SDLK_KP4;
-	case SDLK_RIGHT:
-		return SDLK_KP6;
-	default:
-		throw std::runtime_error("Wrong key!");assert(0);
-	}	
-	#else
 	switch(key)
 	{
 	case SDLK_DOWN:
@@ -500,20 +478,14 @@ SDLKey CGuiHandler::arrowToNum( SDLKey key )
 	default:
 		throw std::runtime_error("Wrong key!");
 	}	
-	#endif // 0
 }
 
-SDLKey CGuiHandler::numToDigit( SDLKey key )
+SDL_Keycode CGuiHandler::numToDigit(SDL_Keycode key)
 {
-#ifdef VCMI_SDL1
-	if(key >= SDLK_KP0 && key <= SDLK_KP9)
-		return SDLKey(key - SDLK_KP0 + SDLK_0);
-#endif // 0
 
 #define REMOVE_KP(keyName) case SDLK_KP_ ## keyName : return SDLK_ ## keyName;
 	switch(key)
 	{
-#ifndef VCMI_SDL1
 		REMOVE_KP(0)
 		REMOVE_KP(1)
 		REMOVE_KP(2)
@@ -524,7 +496,6 @@ SDLKey CGuiHandler::numToDigit( SDLKey key )
 		REMOVE_KP(7)
 		REMOVE_KP(8)
 		REMOVE_KP(9)		
-#endif // VCMI_SDL1		
 		REMOVE_KP(PERIOD)
 		REMOVE_KP(MINUS)
 		REMOVE_KP(PLUS)
@@ -542,22 +513,15 @@ SDLKey CGuiHandler::numToDigit( SDLKey key )
 #undef REMOVE_KP
 }
 
-bool CGuiHandler::isNumKey( SDLKey key, bool number )
+bool CGuiHandler::isNumKey(SDL_Keycode key, bool number)
 {
-	#ifdef VCMI_SDL1
-	if(number)
-		return key >= SDLK_KP0 && key <= SDLK_KP9;
-	else
-		return key >= SDLK_KP0 && key <= SDLK_KP_EQUALS;
-	#else
 	if(number)
 		return key >= SDLK_KP_1 && key <= SDLK_KP_0;
 	else
 		return (key >= SDLK_KP_1 && key <= SDLK_KP_0) || key == SDLK_KP_MINUS || key == SDLK_KP_PLUS || key == SDLK_KP_EQUALS;
-	#endif // 0
 }
 
-bool CGuiHandler::isArrowKey( SDLKey key )
+bool CGuiHandler::isArrowKey(SDL_Keycode key)
 {
 	return key == SDLK_UP || key == SDLK_DOWN || key == SDLK_LEFT || key == SDLK_RIGHT;
 }
@@ -580,6 +544,8 @@ CFramerateManager::CFramerateManager(int rate)
 	this->rate = rate;
 	this->rateticks = (1000.0 / rate);
 	this->fps = 0;
+	this->accumulatedFrames = 0;
+	this->accumulatedTime = 0;
 }
 
 void CFramerateManager::init()
@@ -591,18 +557,28 @@ void CFramerateManager::framerateDelay()
 {
 	ui32 currentTicks = SDL_GetTicks();
 	timeElapsed = currentTicks - lastticks;
-
+	
 	// FPS is higher than it should be, then wait some time
 	if (timeElapsed < rateticks)
 	{
 		SDL_Delay(ceil(this->rateticks) - timeElapsed);
 	}
-	currentTicks = SDL_GetTicks();
+	
+	accumulatedTime += timeElapsed;
+	accumulatedFrames++;
 
-	fps = ceil(1000.0 / timeElapsed);
+	if(accumulatedFrames >= 100)
+	{
+		//about 2 second should be passed
+		fps = ceil(1000.0 / (accumulatedTime/accumulatedFrames));		
+		accumulatedTime = 0;
+		accumulatedFrames = 0;	
+	};	
 
+	currentTicks = SDL_GetTicks();
 	// recalculate timeElapsed for external calls via getElapsed()
 	// limit it to 1000 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
 	timeElapsed = std::min<ui32>(currentTicks - lastticks, 1000);
+
 	lastticks = SDL_GetTicks();
 }

+ 12 - 11
client/gui/CGuiHandler.h

@@ -8,9 +8,9 @@ class CFramerateManager;
 class CGStatusBar;
 class CIntObject;
 class IUpdateable;
-class ILockedUpdatable;
 class IShowActivatable;
 class IShowable;
+template <typename T> struct CondSh;
 
 /*
  * CGuiHandler.h, part of VCMI engine
@@ -29,7 +29,7 @@ private:
 	double rateticks;
 	ui32 lastticks, timeElapsed;
 	int rate;
-
+	ui32 accumulatedTime,accumulatedFrames;
 public:
 	int fps; // the actual fps value
 
@@ -58,10 +58,9 @@ private:
 				   motioninterested,
 	               timeinterested,
 	               wheelInterested,
-	               doubleClickInterested;
-	#ifndef VCMI_SDL1
-	CIntObjectList textInterested;
-	#endif // VCMI_SDL1
+	               doubleClickInterested,
+	               textInterested;
+
 	               
 	void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);               
 public:
@@ -73,7 +72,7 @@ public:
 	std::vector<IShowable*> objsToBlit;
 
 	SDL_Event * current; //current event - can be set to nullptr to stop handling event
-	ILockedUpdatable *curInt;
+	IUpdateable *curInt;
 
 	Point lastClick;
 	unsigned lastClickTime;
@@ -104,12 +103,14 @@ public:
 	ui8 captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
 	std::list<CIntObject *> createdObj; //stack of objs being created
 
-	static SDLKey arrowToNum(SDLKey key); //converts arrow key to according numpad key
-	static SDLKey numToDigit(SDLKey key);//converts numpad digit key to normal digit key
-	static bool isNumKey(SDLKey key, bool number = true); //checks if key is on numpad (numbers - check only for numpad digits)
-	static bool isArrowKey(SDLKey key);
+	static SDL_Keycode arrowToNum(SDL_Keycode key); //converts arrow key to according numpad key
+	static SDL_Keycode numToDigit(SDL_Keycode key);//converts numpad digit key to normal digit key
+	static bool isNumKey(SDL_Keycode key, bool number = true); //checks if key is on numpad (numbers - check only for numpad digits)
+	static bool isArrowKey(SDL_Keycode key);
 	static bool amIGuiThread();
 	static void pushSDLEvent(int type, int usercode = 0);
+	
+	static CondSh<bool> terminate_cond; // confirm termination
 };
 
 extern CGuiHandler GH; //global gui handler

+ 7 - 21
client/gui/CIntObject.h

@@ -15,8 +15,8 @@
 #include "../Graphics.h"
 
 struct SDL_Surface;
-class CPicture;
 class CGuiHandler;
+class CPicture;
 
 struct SDL_KeyboardEvent;
 
@@ -38,13 +38,6 @@ public:
 	virtual ~IUpdateable(){}; //d-tor
 };
 
-class ILockedUpdatable: public IUpdateable
-{
-public:
-	virtual void runLocked(std::function<void()> cb) = 0;
-	virtual ~ILockedUpdatable(){}; //d-tor
-};
-
 // Defines a show method
 class IShowable
 {
@@ -97,11 +90,6 @@ public:
  */
 	std::vector<CIntObject *> children;
 
-	//FIXME: workaround
-	void deactivateKeyboard()
-	{
-		deactivate(KEYBOARD);
-	}
 
 /*
  * Public interface
@@ -133,10 +121,8 @@ public:
 	virtual void keyPressed(const SDL_KeyboardEvent & key){}
 	virtual bool captureThisEvent(const SDL_KeyboardEvent & key); //allows refining captureAllKeys against specific events (eg. don't capture ENTER)
 
-#ifndef VCMI_SDL1
 	virtual void textInputed(const SDL_TextInputEvent & event){};
 	virtual void textEdited(const SDL_TextEditingEvent & event){};
-#endif // VCMI_SDL1
 
 	//mouse movement handling
 	bool strongInterest; //if true - report all mouse movements, if not - only when hovered
@@ -166,15 +152,15 @@ public:
 
 	// activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse)
 	// usually used automatically by parent
-	void activate();
-	void deactivate();
+	void activate() override;
+	void deactivate() override;
 
 	//called each frame to update screen
-	void show(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
 	//called on complete redraw only
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 	//request complete redraw of this object
-	void redraw();
+	void redraw() override;
 
 	enum EAlignment {TOPLEFT, CENTER, BOTTOMRIGHT};
 
@@ -221,5 +207,5 @@ public:
 	CKeyShortcut();
 	CKeyShortcut(int key);
 	CKeyShortcut(std::set<int> Keys);
-	virtual void keyPressed(const SDL_KeyboardEvent & key); //call-in
+	virtual void keyPressed(const SDL_KeyboardEvent & key) override; //call-in
 };

+ 0 - 7
client/gui/Geometries.h

@@ -13,13 +13,6 @@
  *
  */
 
-#ifdef max
-#undef max
-#endif
-#ifdef min
-#undef min
-#endif
-
 struct SDL_MouseMotionEvent;
 
 // A point with x/y coordinate, used mostly for graphic rendering

+ 0 - 11
client/gui/SDL_Compat.h

@@ -13,23 +13,12 @@
 #include <SDL_version.h>
 
 #if (SDL_MAJOR_VERSION == 2)
-#define VCMI_SDL2
 
 #include <SDL_keycode.h>
 typedef int SDLX_Coord;
 typedef int SDLX_Size;
 
-typedef SDL_Keycode SDLKey;
 
-#define SDL_SRCCOLORKEY SDL_TRUE
-
-#define SDL_FULLSCREEN SDL_WINDOW_FULLSCREEN
-
-#elif (SDL_MAJOR_VERSION == 1) 
-#define VCMI_SDL1
-//SDL 1.x
-typedef Sint16 SDLX_Coord;
-typedef Uint16 SDLX_Size;
 #else
 #error "unknown or unsupported SDL version"
 #endif

+ 22 - 54
client/gui/SDL_Extensions.cpp

@@ -4,7 +4,6 @@
 
 #include "../CGameInfo.h"
 #include "../CMessage.h"
-#include "../CDefHandler.h"
 #include "../Graphics.h"
 #include "../CMT.h"
 
@@ -14,7 +13,6 @@ const SDL_Color Colors::METALLIC_GOLD = { 173, 142, 66, 0 };
 const SDL_Color Colors::GREEN = { 0, 255, 0, 0 };
 const SDL_Color Colors::DEFAULT_KEY_COLOR = {0, 255, 255, 0};
 
-#if (SDL_MAJOR_VERSION == 2)
 void SDL_UpdateRect(SDL_Surface *surface, int x, int y, int w, int h)
 {
 	Rect rect(x,y,w,h);
@@ -24,10 +22,9 @@ void SDL_UpdateRect(SDL_Surface *surface, int x, int y, int w, int h)
 	SDL_RenderClear(mainRenderer);
 	if(0 != SDL_RenderCopy(mainRenderer, screenTexture, NULL, NULL))
 		logGlobal->errorStream() << __FUNCTION__ << "SDL_RenderCopy " <<  SDL_GetError();
-	SDL_RenderPresent(mainRenderer);	
-	
+	SDL_RenderPresent(mainRenderer);
+
 }
-#endif // VCMI_SDL1
 
 SDL_Surface * CSDL_Ext::newSurface(int w, int h, SDL_Surface * mod) //creates new surface, with flags/format same as in surface given
 {
@@ -171,7 +168,7 @@ void CSDL_Ext::alphaTransform(SDL_Surface *src)
 		SDL_Color & palColor = src->format->palette->colors[i];
 		palColor = colors[i];
 	}
-	SDL_SetColorKey(src, SDL_SRCCOLORKEY, 0);
+	SDL_SetColorKey(src, SDL_TRUE, 0);
 }
 
 static void prepareOutRect(SDL_Rect *src, SDL_Rect *dst, const SDL_Rect & clip_rect)
@@ -460,11 +457,7 @@ int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const SDL_Rect * sr
 				for(int x = w; x; x--)
 				{
 					const SDL_Color &tbc = colors[*color++]; //color to blit
-					#ifdef VCMI_SDL1
-					ColorPutter<bpp, +1>::PutColorAlphaSwitch(p, tbc.r, tbc.g, tbc.b, tbc.unused);
-					#else
 					ColorPutter<bpp, +1>::PutColorAlphaSwitch(p, tbc.r, tbc.g, tbc.b, tbc.a);
-					#endif // 0					
 				}
 			}
 			SDL_UnlockSurface(dst);
@@ -481,7 +474,7 @@ int CSDL_Ext::blit8bppAlphaTo24bpp(const SDL_Surface * src, const SDL_Rect * src
 	case 3: return blit8bppAlphaTo24bppT<3>(src, srcRect, dst, dstRect);
 	case 4: return blit8bppAlphaTo24bppT<4>(src, srcRect, dst, dstRect);
 	default:
-        logGlobal->errorStream() << (int)dst->format->BitsPerPixel << " bpp is not supported!!!";
+		logGlobal->errorStream() << (int)dst->format->BitsPerPixel << " bpp is not supported!!!";
 		return -1;
 	}
 }
@@ -489,11 +482,7 @@ int CSDL_Ext::blit8bppAlphaTo24bpp(const SDL_Surface * src, const SDL_Rect * src
 Uint32 CSDL_Ext::colorToUint32(const SDL_Color * color)
 {
 	Uint32 ret = 0;
-	#ifdef VCMI_SDL1
-	ret+=color->unused;
-	#else
 	ret+=color->a;
-	#endif // 0	
 	ret<<=8; //*=256
 	ret+=color->b;
 	ret<<=8; //*=256
@@ -505,15 +494,10 @@ Uint32 CSDL_Ext::colorToUint32(const SDL_Color * color)
 
 void CSDL_Ext::update(SDL_Surface * what)
 {
-	#ifdef VCMI_SDL1
-	if(what)
-		SDL_UpdateRect(what, 0, 0, what->w, what->h);	
-	#else
 	if(!what)
 		return;
 	if(0 !=SDL_UpdateTexture(screenTexture, nullptr, what->pixels, what->pitch))
-		logGlobal->errorStream() << __FUNCTION__ << "SDL_UpdateTexture " << SDL_GetError();		
-	#endif // VCMI_SDL1
+		logGlobal->errorStream() << __FUNCTION__ << "SDL_UpdateTexture " << SDL_GetError();
 }
 void CSDL_Ext::drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const int3 &color)
 {
@@ -571,7 +555,7 @@ void CSDL_Ext::setPlayerColor(SDL_Surface * sur, PlayerColor player)
 		SDL_SetColors(sur, color, 5, 1);
 	}
 	else
-        logGlobal->warnStream() << "Warning, setPlayerColor called on not 8bpp surface!";
+		logGlobal->warnStream() << "Warning, setPlayerColor called on not 8bpp surface!";
 }
 
 TColorPutter CSDL_Ext::getPutterFor(SDL_Surface * const &dest, int incrementing)
@@ -592,7 +576,7 @@ case BytesPerPixel:									\
 		CASE_BPP(3)
 		CASE_BPP(4)
 	default:
-        logGlobal->errorStream() << (int)dest->format->BitsPerPixel << "bpp is not supported!";
+		logGlobal->errorStream() << (int)dest->format->BitsPerPixel << "bpp is not supported!";
 		return nullptr;
 	}
 
@@ -606,7 +590,7 @@ TColorPutterAlpha CSDL_Ext::getPutterAlphaFor(SDL_Surface * const &dest, int inc
 		CASE_BPP(3)
 		CASE_BPP(4)
 	default:
-        logGlobal->errorStream() << (int)dest->format->BitsPerPixel << "bpp is not supported!";
+		logGlobal->errorStream() << (int)dest->format->BitsPerPixel << "bpp is not supported!";
 		return nullptr;
 	}
 #undef CASE_BPP
@@ -632,22 +616,14 @@ bool CSDL_Ext::isTransparent( SDL_Surface * srf, int x, int y )
 		return true;
 
 	SDL_Color color;
-	
-	#ifdef VCMI_SDL1
-	SDL_GetRGBA(SDL_GetPixel(srf, x, y), srf->format, &color.r, &color.g, &color.b, &color.unused);
-	#else
+
 	SDL_GetRGBA(SDL_GetPixel(srf, x, y), srf->format, &color.r, &color.g, &color.b, &color.a);
-	#endif // 0	
 
 	// color is considered transparent here if
 	// a) image has aplha: less than 50% transparency
 	// b) no alpha: color is cyan
 	if (srf->format->Amask)
-	#ifdef VCMI_SDL1
-		return color.unused < 128; // almost transparent
-	#else
 		return color.a < 128; // almost transparent
-	#endif // 0				
 	else
 		return (color.r == 0 && color.g == 255 && color.b == 255);
 }
@@ -698,7 +674,7 @@ BlitterWithRotationVal CSDL_Ext::getBlitterWithRotation(SDL_Surface *dest)
 	case 3: return blitWithRotateClipVal<3>;
 	case 4: return blitWithRotateClipVal<4>;
 	default:
-        logGlobal->errorStream() << (int)dest->format->BitsPerPixel << " bpp is not supported!!!";
+		logGlobal->errorStream() << (int)dest->format->BitsPerPixel << " bpp is not supported!!!";
 		break;
 	}
 
@@ -714,7 +690,7 @@ BlitterWithRotationVal CSDL_Ext::getBlitterWithRotationAndAlpha(SDL_Surface *des
 	case 3: return blitWithRotateClipValWithAlpha<3>;
 	case 4: return blitWithRotateClipValWithAlpha<4>;
 	default:
-        logGlobal->errorStream() << (int)dest->format->BitsPerPixel << " bpp is not supported!!!";
+		logGlobal->errorStream() << (int)dest->format->BitsPerPixel << " bpp is not supported!!!";
 		break;
 	}
 
@@ -988,42 +964,34 @@ SDL_Color CSDL_Ext::makeColor(ui8 r, ui8 g, ui8 b, ui8 a)
 
 void CSDL_Ext::startTextInput(SDL_Rect * where)
 {
-	#ifndef VCMI_SDL1
-	if (SDL_IsTextInputActive() == SDL_FALSE)		
-	{		
-		SDL_StartTextInput();		
-	}		
+	if (SDL_IsTextInputActive() == SDL_FALSE)
+	{
+		SDL_StartTextInput();
+	}
 	SDL_SetTextInputRect(where);
-	#endif
 }
 
 void CSDL_Ext::stopTextInput()
 {
-	#ifndef VCMI_SDL1
 	if (SDL_IsTextInputActive() == SDL_TRUE)
-	{		
-		SDL_StopTextInput();			
-	}		
-	#endif	
+	{
+		SDL_StopTextInput();
+	}
 }
 
 STRONG_INLINE static uint32_t mapColor(SDL_Surface * surface, SDL_Color color)
 {
-	#ifdef VCMI_SDL1
-	return SDL_MapRGB(surface->format, color.r, color.g, color.b); 
-	#else
-	return SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a); 
-	#endif		
+	return SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a);
 }
 
 void CSDL_Ext::setColorKey(SDL_Surface * surface, SDL_Color color)
 {
 	uint32_t key = mapColor(surface,color);
-	SDL_SetColorKey(surface, SDL_SRCCOLORKEY, key);	
+	SDL_SetColorKey(surface, SDL_TRUE, key);
 }
 
 void CSDL_Ext::setDefaultColorKey(SDL_Surface * surface)
-{	
+{
 	setColorKey(surface, Colors::DEFAULT_KEY_COLOR);
 }
 
@@ -1034,7 +1002,7 @@ void CSDL_Ext::setDefaultColorKeyPresize(SDL_Surface * surface)
 
 	// set color key only if exactly such color was found
 	if (color.r == Colors::DEFAULT_KEY_COLOR.r && color.g == Colors::DEFAULT_KEY_COLOR.g && color.b == Colors::DEFAULT_KEY_COLOR.b)
-		SDL_SetColorKey(surface, SDL_SRCCOLORKEY, key);	
+		SDL_SetColorKey(surface, SDL_TRUE, key);
 }
 
 

+ 1 - 51
client/gui/SDL_Extensions.h

@@ -10,35 +10,14 @@
  
 #pragma once
 #include <SDL_version.h>
-
-#ifndef VCMI_SDL1
 #include <SDL_render.h>
-#endif
-
 #include <SDL_video.h>
 #include <SDL_events.h>
 #include "../../lib/int3.h"
-//#include "../Graphics.h"
 #include "Geometries.h"
 #include "../../lib/GameConstants.h"
 
 
-//A macro to force inlining some of our functions. Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower
-#ifdef _MSC_VER
-	#define STRONG_INLINE __forceinline
-#elif __GNUC__
-	#define STRONG_INLINE inline __attribute__((always_inline))
-#else
-	#define STRONG_INLINE inline
-#endif
-
-#if SDL_VERSION_ATLEAST(1,3,0)
-#define SDL_GetKeyState SDL_GetKeyboardState
-#endif
-
-//SDL2 support
-#if (SDL_MAJOR_VERSION == 2)
-
 extern SDL_Window * mainWindow;
 extern SDL_Renderer * mainRenderer;
 extern SDL_Texture * screenTexture;
@@ -54,64 +33,35 @@ inline void SDL_WarpMouse(int x, int y)
 }
 
 void SDL_UpdateRect(SDL_Surface *surface, int x, int y, int w, int h);
-#endif
 
 inline bool isCtrlKeyDown()
 {
-	#ifdef VCMI_SDL1
-	return SDL_GetKeyState(nullptr)[SDLK_LCTRL] || SDL_GetKeyState(nullptr)[SDLK_RCTRL];
-	#else
 	return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RCTRL];
-	#endif
 }
 
 inline bool isAltKeyDown()
 {
-	#ifdef VCMI_SDL1
-	return SDL_GetKeyState(nullptr)[SDLK_LALT] || SDL_GetKeyState(nullptr)[SDLK_RALT];
-	#else
 	return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LALT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RALT];
-	#endif
 }
 
 inline bool isShiftKeyDown()
 {
-	#ifdef VCMI_SDL1
-	return SDL_GetKeyState(nullptr)[SDLK_LSHIFT] || SDL_GetKeyState(nullptr)[SDLK_RSHIFT];
-	#else
 	return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LSHIFT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RSHIFT];
-	#endif
 }
 namespace CSDL_Ext
 {
-	STRONG_INLINE void colorSetAlpha(SDL_Color & color, Uint8 alpha)
-	{
-#ifdef VCMI_SDL1
-		color.unused = alpha;
-#else
-		color.a = alpha;
-#endif	
-	}
 	//todo: should this better be assignment operator?
 	STRONG_INLINE void colorAssign(SDL_Color & dest, const SDL_Color & source)
 	{
 		dest.r = source.r;
 		dest.g = source.g;
 		dest.b = source.b;
-#ifdef VCMI_SDL1
-		dest.unused = source.unused;
-#else
 		dest.a = source.a;
-#endif			
 	}
 
 	inline void setAlpha(SDL_Surface * bg, int value)
 	{
-#ifdef VCMI_SDL1
-		SDL_SetAlpha(bg, SDL_SRCALPHA, value);
-#else
 		SDL_SetSurfaceAlphaMod(bg, value);
-#endif
 	}
 }
 struct Rect;
@@ -154,7 +104,7 @@ template<typename IntType>
 std::string makeNumberShort(IntType number, IntType maxLength = 3) //the output is a string containing at most 5 characters [4 if positive] (eg. intead 10000 it gives 10k)
 {
 	IntType max = pow(10, maxLength);
-	if (abs(number) < max)
+	if (std::abs(number) < max)
 		return boost::lexical_cast<std::string>(number);
 
 	std::string symbols = " kMGTPE";

+ 0 - 8
client/gui/SDL_Pixels.h

@@ -135,11 +135,7 @@ struct ColorPutter<2, incrementPtr>
 template<int bpp, int incrementPtr>
 STRONG_INLINE void ColorPutter<bpp, incrementPtr>::PutColorAlpha(Uint8 *&ptr, const SDL_Color & Color)
 {
-	#ifdef VCMI_SDL1
-	PutColor(ptr, Color.r, Color.g, Color.b, Color.unused);
-	#else
 	PutColor(ptr, Color.r, Color.g, Color.b, Color.a);
-	#endif
 }
 
 template<int bpp, int incrementPtr>
@@ -268,11 +264,7 @@ STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColor(Uint8 *&ptr, const Uin
 template <int incrementPtr>
 STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColorAlpha(Uint8 *&ptr, const SDL_Color & Color)
 {
-	#ifdef VCMI_SDL1
-	PutColor(ptr, Color.r, Color.g, Color.b, Color.unused);
-	#else
 	PutColor(ptr, Color.r, Color.g, Color.b, Color.a);
-	#endif
 }
 
 template <int incrementPtr>

+ 123 - 76
client/mapHandler.cpp

@@ -528,7 +528,7 @@ CMapHandler::CMapNormalBlitter::CMapNormalBlitter(CMapHandler * parent)
 	defaultTileRect = Rect(0, 0, tileSize, tileSize);
 }
 
-SDL_Surface * CMapHandler::CMapWorldViewBlitter::objectToIcon(Obj id, si32 subId, PlayerColor owner) const
+IImage * CMapHandler::CMapWorldViewBlitter::objectToIcon(Obj id, si32 subId, PlayerColor owner) const
 {
 	int ownerIndex = 0;
 	if(owner < PlayerColor::PLAYER_LIMIT)
@@ -539,27 +539,27 @@ SDL_Surface * CMapHandler::CMapWorldViewBlitter::objectToIcon(Obj id, si32 subId
 	{
 		ownerIndex = PlayerColor::PLAYER_LIMIT.getNum() * 19;
 	}
-	
+
 	switch(id)
 	{
 	case Obj::MONOLITH_ONE_WAY_ENTRANCE:
 	case Obj::MONOLITH_ONE_WAY_EXIT:
 	case Obj::MONOLITH_TWO_WAY:
-		return info->iconsDef->ourImages[(int)EWorldViewIcon::TELEPORT].bitmap;
+		return info->icons->getImage((int)EWorldViewIcon::TELEPORT);
 	case Obj::SUBTERRANEAN_GATE:
-		return info->iconsDef->ourImages[(int)EWorldViewIcon::GATE].bitmap;
+		return info->icons->getImage((int)EWorldViewIcon::GATE);
 	case Obj::ARTIFACT:
-		return info->iconsDef->ourImages[(int)EWorldViewIcon::ARTIFACT].bitmap;
+		return info->icons->getImage((int)EWorldViewIcon::ARTIFACT);
 	case Obj::TOWN:
-		return info->iconsDef->ourImages[(int)EWorldViewIcon::TOWN + ownerIndex].bitmap;
+		return info->icons->getImage((int)EWorldViewIcon::TOWN + ownerIndex);
 	case Obj::HERO:
-		return info->iconsDef->ourImages[(int)EWorldViewIcon::HERO + ownerIndex].bitmap;
+		return info->icons->getImage((int)EWorldViewIcon::HERO + ownerIndex);
 	case Obj::MINE:
-		return info->iconsDef->ourImages[(int)EWorldViewIcon::MINE_WOOD + subId + ownerIndex].bitmap;
+		return info->icons->getImage((int)EWorldViewIcon::MINE_WOOD + subId + ownerIndex);
 	case Obj::RESOURCE:
-		return info->iconsDef->ourImages[(int)EWorldViewIcon::RES_WOOD + subId + ownerIndex].bitmap;
-	}	
-	return nullptr;	
+		return info->icons->getImage((int)EWorldViewIcon::RES_WOOD + subId + ownerIndex);
+	}
+	return nullptr;
 }
 
 void CMapHandler::CMapWorldViewBlitter::calculateWorldViewCameraPos()
@@ -645,13 +645,13 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
 {
 	auto drawIcon = [this,targetSurf](Obj id, si32 subId, PlayerColor owner)
 	{
-		SDL_Surface * wvIcon = this->objectToIcon(id, subId, owner);
+		IImage * wvIcon = this->objectToIcon(id, subId, owner);
 
-		if (nullptr != wvIcon)
+		if(nullptr != wvIcon)
 		{
 			// centering icon on the object
-			Rect destRect(realPos.x + tileSize / 2 - wvIcon->w / 2, realPos.y + tileSize / 2 - wvIcon->h / 2, wvIcon->w, wvIcon->h);
-			CSDL_Ext::blitSurface(wvIcon, nullptr, targetSurf, &destRect);
+			Point dest(realPos.x + tileSize / 2 - wvIcon->width() / 2, realPos.y + tileSize / 2 - wvIcon->height() / 2);
+			wvIcon->draw(targetSurf, dest.x, dest.y);
 		}
 	};
 
@@ -660,7 +660,7 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
 	{
 		const CGObjectInstance * obj = object.obj;
 
-		const bool sameLevel = obj->pos.z == pos.z;			
+		const bool sameLevel = obj->pos.z == pos.z;
 		const bool isVisible = (*info->visibilityMap)[pos.x][pos.y][pos.z];
 		const bool isVisitable = obj->visitableAt(pos.x, pos.y);
 
@@ -673,31 +673,31 @@ void CMapHandler::CMapWorldViewBlitter::drawOverlayEx(SDL_Surface * targetSurf)
 {
 	if(nullptr == info->additionalIcons)
 		return;
-		
+
 	const int3 bottomRight = pos + tileCount;
-	
+
 	for(const ObjectPosInfo & iconInfo : *(info->additionalIcons))
 	{
 		if( iconInfo.pos.z != pos.z)
 			continue;
-		
+
 		if((iconInfo.pos.x < topTile.x) || (iconInfo.pos.y < topTile.y))
 			continue;
-		
+
 		if((iconInfo.pos.x > bottomRight.x) || (iconInfo.pos.y > bottomRight.y))
-			continue;		
-		
+			continue;
+
 		realPos.x = initPos.x + (iconInfo.pos.x - topTile.x) * tileSize;
 		realPos.y = initPos.x + (iconInfo.pos.y - topTile.y) * tileSize;
-		
-		SDL_Surface * wvIcon = this->objectToIcon(iconInfo.id, iconInfo.subId, iconInfo.owner);				
-		
-		if (nullptr != wvIcon)
+
+		IImage * wvIcon = this->objectToIcon(iconInfo.id, iconInfo.subId, iconInfo.owner);
+
+		if(nullptr != wvIcon)
 		{
 			// centering icon on the object
-			Rect destRect(realPos.x + tileSize / 2 - wvIcon->w / 2, realPos.y + tileSize / 2 - wvIcon->h / 2, wvIcon->w, wvIcon->h);
-			CSDL_Ext::blitSurface(wvIcon, nullptr, targetSurf, &destRect);
-		}		
+			Point dest(realPos.x + tileSize / 2 - wvIcon->width() / 2, realPos.y + tileSize / 2 - wvIcon->height() / 2);
+			wvIcon->draw(targetSurf, dest.x, dest.y);
+		}
 	}
 }
 
@@ -778,8 +778,8 @@ void CMapHandler::CMapPuzzleViewBlitter::drawObjects(SDL_Surface * targetSurf, c
 	// grail X mark
 	if(pos.x == info->grailPos.x && pos.y == info->grailPos.y)
 	{
-		Rect destRect(realTileRect);
-		CSDL_Ext::blit8bppAlphaTo24bpp(graphics->heroMoveArrows->ourImages[0].bitmap, nullptr, targetSurf, &destRect);
+		const IImage * mark = graphics->heroMoveArrows->getImage(0);
+		mark->draw(targetSurf,realTileRect.x,realTileRect.y);
 	}
 }
 
@@ -837,7 +837,7 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
 	for(auto & object : objects)
 	{
 		if (object.fadeAnimKey >= 0)
-		{			
+		{
 			auto fadeIter = parent->fadeAnims.find(object.fadeAnimKey);
 			if (fadeIter != parent->fadeAnims.end())
 			{
@@ -850,27 +850,34 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
 			logGlobal->errorStream() << "Fading map object with missing fade anim : " << object.fadeAnimKey;
 			continue;
 		}
-		
+
 		const CGObjectInstance * obj = object.obj;
 		if (!obj)
 		{
 			logGlobal->errorStream() << "Stray map object that isn't fading";
 			continue;
 		}
-		
+
 		if (!graphics->getDef(obj))
 			processDef(obj->appearance);
-		if (!graphics->getDef(obj) && !obj->appearance.animationFile.empty())
-			logGlobal->errorStream() << "Failed to load image " << obj->appearance.animationFile;
+		if (!graphics->getDef(obj))
+		{
+			if (!obj->appearance.animationFile.empty())
+				logGlobal->errorStream() << "Failed to load image " << obj->appearance.animationFile;
+			else
+				logGlobal->warnStream() << boost::format("Def name for obj %d (%d,%d) is empty!") % obj->id % obj->ID % obj->subID;
+
+			continue;
+		}
 
 		if (!canDrawObject(obj))
 			continue;
-		
+
 		auto objData = findObjectBitmap(obj, info->anim);
 		if (objData.objBitmap)
-		{			
+		{
 			Rect srcRect(object.rect.x, object.rect.y, tileSize, tileSize);
-			
+
 			drawObject(targetSurf, objData.objBitmap, &srcRect, objData.isMoving);
 			if (objData.flagBitmap)
 			{
@@ -883,7 +890,7 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
 				else if (obj->pos.x == pos.x && obj->pos.y == pos.y)
 				{
 					Rect dstRect(realPos.x - 2 * tileSize, realPos.y - tileSize, 3 * tileSize, 2 * tileSize);
-					drawHeroFlag(targetSurf, objData.flagBitmap, nullptr, &dstRect, false);					
+					drawHeroFlag(targetSurf, objData.flagBitmap, nullptr, &dstRect, false);
 				}
 			}
 		}
@@ -939,7 +946,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 		{
 			if (pos.y < 0 || pos.y >= parent->sizes.y)
 				continue;
-			
+
 			const bool isVisible = canDrawCurrentTile();
 
 			realTileRect.x = realPos.x;
@@ -948,13 +955,13 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 			const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
 			const TerrainTile & tinfo = parent->map->getTile(pos);
 			const TerrainTile * tinfoUpper = pos.y > 0 ? &parent->map->getTile(int3(pos.x, pos.y - 1, pos.z)) : nullptr;
-			
+
 			if(isVisible || info->showAllTerrain)
 			{
 				drawTileTerrain(targetSurf, tinfo, tile);
 				if (tinfo.riverType)
 					drawRiver(targetSurf, tinfo);
-				drawRoad(targetSurf, tinfo, tinfoUpper);				
+				drawRoad(targetSurf, tinfo, tinfoUpper);
 			}
 
 			if(isVisible)
@@ -1010,8 +1017,8 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 			}
 		}
 	}
-	
-	drawOverlayEx(targetSurf);	
+
+	drawOverlayEx(targetSurf);
 
 	// drawDebugGrid()
 	if (settings["session"]["showGrid"].Bool())
@@ -1084,10 +1091,10 @@ CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findBoatBitmap(const CGB
 }
 
 SDL_Surface * CMapHandler::CMapBlitter::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int indexOffset) const
-{	
+{
 	if (!hero)
 		return nullptr;
-	
+
 	if (hero->boat)
 		return findBoatFlagBitmap(hero->boat, anim, color, indexOffset, hero->moveDir);
 	return findHeroFlagBitmap(hero, anim, color, indexOffset);
@@ -1114,8 +1121,8 @@ SDL_Surface * CMapHandler::CMapBlitter::findBoatFlagBitmap(const CGBoat * boat,
 SDL_Surface * CMapHandler::CMapBlitter::findFlagBitmapInternal(const CDefEssential * def, int anim, int indexOffset, ui8 dir, bool moving) const
 {
 	if (moving)
-		return def->ourImages[indexOffset + anim % FRAMES_PER_MOVE_ANIM_GROUP].bitmap;	
-	return def->ourImages[getHeroFrameNum(dir, false) * FRAMES_PER_MOVE_ANIM_GROUP + (anim / 4) % FRAMES_PER_MOVE_ANIM_GROUP].bitmap;	
+		return def->ourImages[indexOffset + anim % FRAMES_PER_MOVE_ANIM_GROUP].bitmap;
+	return def->ourImages[getHeroFrameNum(dir, false) * FRAMES_PER_MOVE_ANIM_GROUP + (anim / 4) % FRAMES_PER_MOVE_ANIM_GROUP].bitmap;
 }
 
 int CMapHandler::CMapBlitter::findAnimIndexByGroup(const CDefEssential * def, int groupNum) const
@@ -1134,9 +1141,9 @@ CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findObjectBitmap(const C
 		return findHeroBitmap(static_cast<const CGHeroInstance*>(obj), anim);
 	if (obj->ID == Obj::BOAT)
 		return findBoatBitmap(static_cast<const CGBoat*>(obj), anim);
-	
-	// normal object	
-	
+
+	// normal object
+
 	const std::vector<Cimage> &ourImages = graphics->getDef(obj)->ourImages;
 	SDL_Surface *bitmap = ourImages[(anim + getPhaseShift(obj)) % ourImages.size()].bitmap;
 
@@ -1207,14 +1214,14 @@ std::pair<SDL_Surface *, bool> CMapHandler::CMapBlitter::getVisBitmap() const
 }
 
 bool CMapHandler::updateObjectsFade()
-{	
+{
 	for (auto iter = fadeAnims.begin(); iter != fadeAnims.end(); )
 	{
 		int3 pos = (*iter).second.first;
 		CFadeAnimation * anim = (*iter).second.second;
-		
+
 		anim->update();
-		
+
 		if (anim->isFading())
 			++iter;
 		else // fade finished
@@ -1223,7 +1230,7 @@ bool CMapHandler::updateObjectsFade()
 			for (auto objIter = objs.begin(); objIter != objs.end(); ++objIter)
 			{
 				if ((*objIter).fadeAnimKey == (*iter).first)
-				{						
+				{
 					logAnim->traceStream() << "Fade anim finished for obj at " << pos << "; remaining: " << (fadeAnims.size() - 1);
 					if (anim->fadingMode == CFadeAnimation::EMode::OUT)
 						objs.erase(objIter); // if this was fadeout, remove the object from the map
@@ -1232,10 +1239,11 @@ bool CMapHandler::updateObjectsFade()
 					break;
 				}
 			}
+			delete (*iter).second.second;
 			iter = fadeAnims.erase(iter);
 		}
 	}
-	
+
 	return !fadeAnims.empty();
 }
 
@@ -1243,7 +1251,7 @@ bool CMapHandler::startObjectFade(TerrainTileObject & obj, bool in, int3 pos)
 {
 	SDL_Surface * fadeBitmap;
 	assert(obj.obj);
-	
+
 	auto objData = normalBlitter->findObjectBitmap(obj.obj, 0);
 	if (objData.objBitmap)
 	{
@@ -1252,7 +1260,7 @@ bool CMapHandler::startObjectFade(TerrainTileObject & obj, bool in, int3 pos)
 			logAnim->debugStream() << "Ignoring fade of moving object";
 			return false;
 		}
-				
+
 		fadeBitmap = CSDL_Ext::newSurface(32, 32); // TODO cache these bitmaps instead of creating new ones?
 		Rect objSrcRect(obj.rect.x, obj.rect.y, 32, 32);
 		CSDL_Ext::blit8bppAlphaTo24bpp(objData.objBitmap, &objSrcRect, fadeBitmap, nullptr);
@@ -1268,12 +1276,12 @@ bool CMapHandler::startObjectFade(TerrainTileObject & obj, bool in, int3 pos)
 		anim->init(in ? CFadeAnimation::EMode::IN : CFadeAnimation::EMode::OUT, fadeBitmap, true);
 		fadeAnims[++fadeAnimCounter] = std::pair<int3, CFadeAnimation*>(pos, anim);
 		obj.fadeAnimKey = fadeAnimCounter;
-		
-		logAnim->traceStream() << "Fade anim started for obj " << obj.obj->ID 
+
+		logAnim->traceStream() << "Fade anim started for obj " << obj.obj->ID
 							   << " at " << pos << "; anim count: " << fadeAnims.size();
 		return true;
 	}
-	
+
 	return false;
 }
 
@@ -1296,17 +1304,17 @@ bool CMapHandler::printObject(const CGObjectInstance *obj, bool fadein /* = fals
 			cr.x = fx*32;
 			cr.y = fy*32;
 			TerrainTileObject toAdd(obj, cr);
-			
+
 			if((obj->pos.x + fx - tilesW+1)>=0 && (obj->pos.x + fx - tilesW+1)<ttiles.size()-frameW && (obj->pos.y + fy - tilesH+1)>=0 && (obj->pos.y + fy - tilesH+1)<ttiles[0].size()-frameH)
 			{
 				int3 pos(obj->pos.x + fx - tilesW + 1, obj->pos.y + fy - tilesH + 1, obj->pos.z);
-				TerrainTile2 & curt = ttiles[pos.x][pos.y][pos.z];					
-				
+				TerrainTile2 & curt = ttiles[pos.x][pos.y][pos.z];
+
 				if (fadein && ADVOPT.objectFading)
 				{
 					startObjectFade(toAdd, true, pos);
 				}
-				
+
 				auto i = curt.objects.begin();
 				for(; i != curt.objects.end(); i++)
 				{
@@ -1322,22 +1330,54 @@ bool CMapHandler::printObject(const CGObjectInstance *obj, bool fadein /* = fals
 					curt.objects.insert(i, toAdd);
 			}
 
-		} // for(int fy=0; fy<tilesH; ++fy)
-	} //for(int fx=0; fx<tilesW; ++fx)
+		}
+	}
 	return true;
 }
 
 bool CMapHandler::hideObject(const CGObjectInstance *obj, bool fadeout /* = false */)
 {
-	// do we actually need to search through the whole map for this?
-	for (size_t i=0; i<map->width; i++)
-	{
-		for (size_t j=0; j<map->height; j++)
+	//optimized version which reveals weird bugs with missing def name
+	//auto pos = obj->pos;
+
+	//for (size_t i = pos.x; i > pos.x - obj->getWidth(); i--)
+	//{
+	//	for (size_t j = pos.y; j > pos.y - obj->getHeight(); j--)
+	//	{
+	//		int3 t(i, j, pos.z);
+	//		if (!map->isInTheMap(t))
+	//			continue;
+
+	//		auto &objs = ttiles[i][j][pos.z].objects;
+	//		for (size_t x = 0; x < objs.size(); x++)
+	//		{
+	//			auto ourObj = objs[x].obj;
+	//			if (ourObj && ourObj->id == obj->id)
+	//			{
+	//				if (fadeout && ADVOPT.objectFading) // object should be faded == erase is delayed until the end of fadeout
+	//				{
+	//					if (startObjectFade(objs[x], false, t))
+	//						objs[x].obj = nullptr; //set original pointer to null
+	//					else
+	//						objs.erase(objs.begin() + x);
+	//				}
+	//				else
+	//					objs.erase(objs.begin() + x);
+	//				break;
+	//			}
+	//		}
+	//	}
+
+	//}
+
+	for (size_t i = 0; i<map->width; i++)
+	{
+		for (size_t j = 0; j<map->height; j++)
 		{
-			for (size_t k=0; k<(map->twoLevel ? 2 : 1); k++)
+			for (size_t k = 0; k<(map->twoLevel ? 2 : 1); k++)
 			{
 				auto &objs = ttiles[i][j][k].objects;
-				for(size_t x=0; x < objs.size(); x++)
+				for (size_t x = 0; x < objs.size(); x++)
 				{
 					if (objs[x].obj && objs[x].obj->id == obj->id)
 					{
@@ -1356,6 +1396,7 @@ bool CMapHandler::hideObject(const CGObjectInstance *obj, bool fadeout /* = fals
 			}
 		}
 	}
+
 	return true;
 }
 bool CMapHandler::removeObject(CGObjectInstance *obj, bool fadeout /* = false */)
@@ -1503,7 +1544,7 @@ CMapHandler::~CMapHandler()
 		for(int j=0; j < elem.size(); ++j)
 			SDL_FreeSurface(elem[j]);
 	}
-	
+
 	for (auto & elem : fadeAnims)
 	{
 		delete elem.second.second;
@@ -1536,10 +1577,16 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN
 		}
 	}
 
-	if(t.hasFavourableWinds())
+	if(t.hasFavorableWinds())
 		out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS);
 	else if(terName)
+	{
 		out = CGI->generaltexth->terrainNames[t.terType];
+		if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG)
+		{
+			out = boost::str(boost::format("%s %s") % out % CGI->generaltexth->allTexts[330]); /// digging ok
+		}
+	}
 }
 
 void CMapHandler::discardWorldViewCache()

+ 19 - 17
client/mapHandler.h

@@ -26,6 +26,8 @@ struct TerrainTile;
 struct SDL_Surface;
 struct SDL_Rect;
 class CDefEssential;
+class CAnimation;
+class IImage;
 class CFadeAnimation;
 class PlayerColor;
 
@@ -71,7 +73,7 @@ struct TerrainTileObject
 	const CGObjectInstance *obj;
 	SDL_Rect rect;
 	int fadeAnimKey;
-	
+
 	TerrainTileObject(const CGObjectInstance *obj_, SDL_Rect rect_);
 	~TerrainTileObject();
 };
@@ -90,7 +92,7 @@ struct MapDrawingInfo
 	int3 &topTile; // top-left tile in viewport [in tiles]
 	const std::vector< std::vector< std::vector<ui8> > > * visibilityMap;
 	SDL_Rect * drawBounds; // map rect drawing bounds on screen
-	CDefHandler * iconsDef; // holds overlay icons for world view mode
+	std::shared_ptr<CAnimation> icons; // holds overlay icons for world view mode
 	float scale; // map scale for world view mode (only if scaled == true)
 
 	bool otherheroAnim;
@@ -101,17 +103,17 @@ struct MapDrawingInfo
 
 	bool puzzleMode;
 	int3 grailPos; // location of grail for puzzle mode [in tiles]
-	
+
 	const std::vector<ObjectPosInfo> * additionalIcons;
-	
+
 	bool showAllTerrain; //for expert viewEarth
-	
-	MapDrawingInfo(int3 &topTile_, const std::vector< std::vector< std::vector<ui8> > > * visibilityMap_, SDL_Rect * drawBounds_, CDefHandler * iconsDef_ = nullptr)
+
+	MapDrawingInfo(int3 &topTile_, const std::vector< std::vector< std::vector<ui8> > > * visibilityMap_, SDL_Rect * drawBounds_, std::shared_ptr<CAnimation> icons_ = nullptr)
 		: scaled(false),
 		  topTile(topTile_),
 		  visibilityMap(visibilityMap_),
 		  drawBounds(drawBounds_),
-		  iconsDef(iconsDef_),
+		  icons(icons_),
 		  scale(1.0f),
 		  otherheroAnim(false),
 		  anim(0u),
@@ -188,14 +190,14 @@ class CMapHandler
 		SDL_Surface * cacheWorldViewEntry(EMapCacheType type, intptr_t key, SDL_Surface * entry);
 		intptr_t genKey(intptr_t realPtr, ui8 mod);
 	};
-	
+
 	/// helper struct to pass around resolved bitmaps of an object; surfaces can be nullptr if object doesn't have bitmap of that type
 	struct AnimBitmapHolder
 	{
 		SDL_Surface * objBitmap; // main object bitmap
 		SDL_Surface * flagBitmap; // flag bitmap for the object (probably only for heroes and boats with heroes)
 		bool isMoving; // indicates if the object is moving (again, heroes/boats only)
-		
+
 		AnimBitmapHolder(SDL_Surface * objBitmap_ = nullptr, SDL_Surface * flagBitmap_ = nullptr, bool moving = false)
 			: objBitmap(objBitmap_),
 			  flagBitmap(flagBitmap_),
@@ -205,7 +207,7 @@ class CMapHandler
 
 
 	class CMapBlitter
-	{		
+	{
 	protected:
 		const int FRAMES_PER_MOVE_ANIM_GROUP = 8;
 		CMapHandler * parent; // ptr to enclosing map handler; generally for legacy reasons, probably could/should be refactored out of here
@@ -267,16 +269,16 @@ class CMapHandler
 
 		virtual bool canDrawObject(const CGObjectInstance * obj) const;
 		virtual bool canDrawCurrentTile() const;
-		
+
 		// internal helper methods to choose correct bitmap(s) for object; called internally by findObjectBitmap
 		AnimBitmapHolder findHeroBitmap(const CGHeroInstance * hero, int anim) const;
-		AnimBitmapHolder findBoatBitmap(const CGBoat * hero, int anim) const;		
+		AnimBitmapHolder findBoatBitmap(const CGBoat * hero, int anim) const;
 		SDL_Surface * findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int indexOffset) const;
 		SDL_Surface * findHeroFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int indexOffset) const;
 		SDL_Surface * findBoatFlagBitmap(const CGBoat * obj, int anim, const PlayerColor * color, int indexOffset, ui8 dir) const;
 		SDL_Surface * findFlagBitmapInternal(const CDefEssential * def, int anim, int indexOffset, ui8 dir, bool moving) const;
 		int findAnimIndexByGroup(const CDefEssential * def, int groupNum) const;
-		
+
 	public:
 		CMapBlitter(CMapHandler * p) : parent(p) {}
 		virtual ~CMapBlitter(){}
@@ -303,7 +305,7 @@ class CMapHandler
 	class CMapWorldViewBlitter : public CMapBlitter
 	{
 	private:
-		SDL_Surface * objectToIcon(Obj id, si32 subId, PlayerColor owner) const;
+		IImage * objectToIcon(Obj id, si32 subId, PlayerColor owner) const;
 	protected:
 		void drawElement(EMapCacheType cacheType, SDL_Surface * sourceSurf, SDL_Rect * sourceRect,
 						 SDL_Surface * targetSurf, SDL_Rect * destRect, bool alphaBlit = false, ui8 rotationInfo = 0u) const override;
@@ -312,7 +314,7 @@ class CMapHandler
 		void drawHeroFlag(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const override;
 		void drawObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const override;
 		void drawFrame(SDL_Surface * targetSurf) const override {}
-		void drawOverlayEx(SDL_Surface * targetSurf);
+		void drawOverlayEx(SDL_Surface * targetSurf) override;
 		void init(const MapDrawingInfo * info) override;
 		SDL_Rect clip(SDL_Surface * targetSurf) const override;
 
@@ -344,7 +346,7 @@ class CMapHandler
 	CMapBlitter * normalBlitter;
 	CMapBlitter * worldViewBlitter;
 	CMapBlitter * puzzleViewBlitter;
-	
+
 	std::map<int, std::pair<int3, CFadeAnimation*>> fadeAnims;
 	int fadeAnimCounter;
 
@@ -386,7 +388,7 @@ public:
 
 	void getTerrainDescr(const int3 &pos, std::string & out, bool terName); //if tername == false => empty string when tile is clear
 	CGObjectInstance * createObject(int id, int subid, int3 pos, int owner=254); //creates a new object with a certain id and subid
-	bool printObject(const CGObjectInstance * obj, bool fadein = false); //puts appropriate things to ttiles, so obj will be visible on map
+	bool printObject(const CGObjectInstance * obj, bool fadein = false); //puts appropriate things to tiles, so obj will be visible on map
 	bool hideObject(const CGObjectInstance * obj, bool fadeout = false); //removes appropriate things from ttiles, so obj will be no longer visible on map (but still will exist)
 	bool removeObject(CGObjectInstance * obj, bool fadeout = false); //removes object from each place in VCMI (I hope)
 	void init();

+ 15 - 41
client/widgets/AdventureMapClasses.cpp

@@ -17,6 +17,8 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Pixels.h"
 
+#include "../widgets/Images.h"
+
 #include "../windows/InfoWindows.h"
 #include "../windows/CAdvmapInterface.h"
 #include "../windows/GUIClasses.h"
@@ -1121,24 +1123,11 @@ void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
 		}
 	default:
 		{
-			#ifdef VCMI_SDL1
-			if(enteredText.size() > 0 && enteredText.size() < conf.go()->ac.inputLineLength)
-			{
-				if( key.keysym.unicode < 0x80 && key.keysym.unicode > 0 )
-				{
-					enteredText[enteredText.size()-1] = (char)key.keysym.unicode;
-					enteredText += "_";
-					refreshEnteredText();
-				}
-			}
-			#endif // VCMI_SDL1
 			break;
 		}
 	}
 }
 
-#ifndef VCMI_SDL1
-
 void CInGameConsole::textInputed(const SDL_TextInputEvent & event)
 {
 	if(!captureAllKeys || enteredText.size() == 0)
@@ -1156,8 +1145,6 @@ void CInGameConsole::textEdited(const SDL_TextEditingEvent & event)
  //do nothing here
 }
 
-#endif // VCMI_SDL1
-
 void CInGameConsole::startEnteringText()
 {
 	CSDL_Ext::startTextInput(&pos);
@@ -1219,14 +1206,9 @@ void CInGameConsole::refreshEnteredText()
 
 CInGameConsole::CInGameConsole() : prevEntDisp(-1), defaultTimeout(10000), maxDisplayedTexts(10)
 {
-	#ifdef VCMI_SDL1
-	addUsedEvents(KEYBOARD);
-	#else
 	addUsedEvents(KEYBOARD | TEXTINPUT);
-	#endif
 }
 
-
 CAdvMapPanel::CAdvMapPanel(SDL_Surface * bg, Point position)
 	: CIntObject(),
 	  background(bg)
@@ -1276,11 +1258,11 @@ void CAdvMapPanel::addChildToPanel(CIntObject * obj, ui8 actions /* = 0 */)
 	addChild(obj, false);
 }
 
-CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(SDL_Surface * bg, Point position, int spaceBottom, const PlayerColor &color)
-	: CAdvMapPanel(bg, position)	  
+CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, SDL_Surface * bg, Point position, int spaceBottom, const PlayerColor &color)
+	: CAdvMapPanel(bg, position), icons(_icons)
 {
 	fillerHeight = bg ? spaceBottom - pos.y - pos.h : 0;
-	
+
 	if (fillerHeight > 0)
 	{
 		tmpBackgroundFiller = CMessage::drawDialogBox(pos.w, fillerHeight, color);
@@ -1295,23 +1277,16 @@ CAdvMapWorldViewPanel::~CAdvMapWorldViewPanel()
 		SDL_FreeSurface(tmpBackgroundFiller);
 }
 
-void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor &color, const CDefHandler *def, int indexOffset)
+void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor &color, int indexOffset)
 {
-	for (auto &pic : currentIcons)
-	{
-		removeChild(pic);
-		delete pic;
-	}
-	currentIcons.clear();
+	assert(iconsData.size() == currentIcons.size());
 
-	for (auto &data : iconsData)
+	for(size_t idx = 0; idx < iconsData.size(); idx++)
 	{
-		auto pic = new CPicture(def->ourImages[data.first + indexOffset].bitmap, data.second.x, data.second.y, false);
-		pic->recActions |= SHOWALL;
-		currentIcons.push_back(pic);
-		addChildToPanel(pic);
+		const auto & data = iconsData.at(idx);
+		currentIcons[idx]->setFrame(data.first + indexOffset);
 	}
-	
+
 	if (fillerHeight > 0)
 	{
 		if (tmpBackgroundFiller)
@@ -1320,18 +1295,17 @@ void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor &color, const CDefHan
 	}
 }
 
-void CAdvMapWorldViewPanel::addChildIcon(std::pair<int, Point> data, const CDefHandler *def, int indexOffset)
+void CAdvMapWorldViewPanel::addChildIcon(std::pair<int, Point> data, int indexOffset)
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	iconsData.push_back(data);
-	auto pic = new CPicture(def->ourImages[data.first + indexOffset].bitmap, data.second.x, data.second.y, false);
-	currentIcons.push_back(pic);
-	addChildToPanel(pic);
+	currentIcons.push_back(new CAnimImage(icons, data.first + indexOffset, 0, data.second.x, data.second.y));
 }
 
 void CAdvMapWorldViewPanel::showAll(SDL_Surface * to)
 {
 	if (tmpBackgroundFiller)
-	{		
+	{
 		blitAt(tmpBackgroundFiller, pos.x, pos.y + pos.h, to);
 	}
 

+ 35 - 34
client/widgets/AdventureMapClasses.h

@@ -4,6 +4,8 @@
 #include "../../lib/FunctionList.h"
 
 class CArmedInstance;
+class CAnimation;
+class CAnimImage;
 class CShowableAnim;
 class CGGarrison;
 class CGObjectInstance;
@@ -37,9 +39,9 @@ protected:
 		CListItem(CList * parent);
 		~CListItem();
 
-		void clickRight(tribool down, bool previousState);
-		void clickLeft(tribool down, bool previousState);
-		void hover(bool on);
+		void clickRight(tribool down, bool previousState) override;
+		void clickLeft(tribool down, bool previousState) override;
+		void hover(bool on) override;
 		void onSelect(bool on);
 
 		/// create object with selection rectangle
@@ -118,12 +120,12 @@ class CHeroList	: public CList
 
 		CHeroItem(CHeroList *parent, const CGHeroInstance * hero);
 
-		CIntObject * genSelection();
+		CIntObject * genSelection() override;
 		void update();
-		void select(bool on);
-		void open();
-		void showTooltip();
-		std::string getHoverText();
+		void select(bool on) override;
+		void open() override;
+		void showTooltip() override;
+		std::string getHoverText() override;
 	};
 
 	CIntObject * createHeroItem(size_t index);
@@ -152,12 +154,12 @@ class CTownList	: public CList
 
 		CTownItem(CTownList *parent, const CGTownInstance * town);
 
-		CIntObject * genSelection();
+		CIntObject * genSelection() override;
 		void update();
-		void select(bool on);
-		void open();
-		void showTooltip();
-		std::string getHoverText();
+		void select(bool on) override;
+		void open() override;
+		void showTooltip() override;
+		std::string getHoverText() override;
 	};
 
 	CIntObject * createTownItem(size_t index);
@@ -195,7 +197,7 @@ public:
 	CMinimapInstance(CMinimap * parent, int level);
 	~CMinimapInstance();
 
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 	void tileToPixels (const int3 &tile, int &x, int &y,int toX = 0, int toY = 0);
 
 	void refreshTile(const int3 &pos);
@@ -213,10 +215,10 @@ protected:
 	//to initialize colors
 	std::map<int, std::pair<SDL_Color, SDL_Color> > loadColors(std::string from);
 
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void hover (bool on);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
+	void hover (bool on) override;
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
 
 	void moveAdvMapSelection();
 
@@ -232,7 +234,7 @@ public:
 	void setLevel(int level);
 	void setAIRadar(bool on);
 
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 
 	void hideTile(const int3 &pos); //puts FoW
 	void showTile(const int3 &pos); //removes FoW
@@ -256,7 +258,7 @@ class CInfoBar : public CIntObject
 	public:
 		CVisibleInfo(Point position);
 
-		void show(SDL_Surface *to);
+		void show(SDL_Surface *to) override;
 
 		//functions that must be called only once
 		void loadHero(const CGHeroInstance * hero);
@@ -283,11 +285,11 @@ class CInfoBar : public CIntObject
 	//removes all information about current state, deactivates timer (if any)
 	void reset(EState newState);
 
-	void tick();
+	void tick() override;
 
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void hover(bool on);
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
+	void hover(bool on) override;
 
 public:
 	CInfoBar(const Rect & pos);
@@ -331,7 +333,7 @@ public:
 	/// recolors all buttons to given player color
 	void setPlayerColor(const PlayerColor & clr);
 
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 };
 
 /// specialized version of CAdvMapPanel that handles recolorable def-based pictures for world view info panel
@@ -340,18 +342,19 @@ class CAdvMapWorldViewPanel : public CAdvMapPanel
 	/// data that allows reconstruction of panel info icons
 	std::vector<std::pair<int, Point>> iconsData;
 	/// ptrs to child-pictures constructed from iconsData
-	std::vector<CPicture *> currentIcons;
+	std::vector<CAnimImage *> currentIcons;
 	/// temporary surface drawn below world view panel on higher resolutions (won't be needed when world view panel is configured for extraResolutions mod)
 	SDL_Surface * tmpBackgroundFiller;
 	int fillerHeight;
+	std::shared_ptr<CAnimation> icons;
 public:
-	CAdvMapWorldViewPanel(SDL_Surface * bg, Point position, int spaceBottom, const PlayerColor &color);
+	CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, SDL_Surface * bg, Point position, int spaceBottom, const PlayerColor &color);
 	virtual ~CAdvMapWorldViewPanel();
 
-	void addChildIcon(std::pair<int, Point> data, const CDefHandler *def, int indexOffset);
+	void addChildIcon(std::pair<int, Point> data, int indexOffset);
 	/// recreates all pictures from given def to recolor them according to current player color
-	void recolorIcons(const PlayerColor &color, const CDefHandler *def, int indexOffset);
-	void showAll(SDL_Surface * to);
+	void recolorIcons(const PlayerColor &color, int indexOffset);
+	void showAll(SDL_Surface * to) override;
 };
 
 class CInGameConsole : public CIntObject
@@ -365,14 +368,12 @@ private:
 	int maxDisplayedTexts; //hiw many texts can be displayed simultaneously
 public:
 	std::string enteredText;
-	void show(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
 	void print(const std::string &txt);
-	void keyPressed (const SDL_KeyboardEvent & key); //call-in
+	void keyPressed (const SDL_KeyboardEvent & key) override; //call-in
 
-#ifndef VCMI_SDL1
 	void textInputed(const SDL_TextInputEvent & event) override;
 	void textEdited(const SDL_TextEditingEvent & event) override;
-#endif // VCMI_SDL1
 
 	void startEnteringText();
 	void endEnteringText(bool printEnteredText);

+ 81 - 25
client/widgets/Buttons.cpp

@@ -209,10 +209,10 @@ void CButton::hover (bool on)
 		setState(PRESSED);*/
 
 	std::string name = hoverTexts[getState()].empty()
-		? hoverTexts[getState()]
-		: hoverTexts[0];
+		? hoverTexts[0]
+		: hoverTexts[getState()];
 
-	if(!name.empty() && !isBlocked()) //if there is no name, there is nohing to display also
+	if(!name.empty() && !isBlocked()) //if there is no name, there is nothing to display also
 	{
 		if (LOCPLINT && LOCPLINT->battleInt) //for battle buttons
 		{
@@ -272,10 +272,11 @@ void CButton::setIndex(size_t index, bool playerColoredButton)
 	if (index == currentImage || index>=imageNames.size())
 		return;
 	currentImage = index;
-	setImage(new CAnimation(imageNames[index]), playerColoredButton);
+	auto anim = std::make_shared<CAnimation>(imageNames[index]);
+	setImage(anim, playerColoredButton);
 }
 
-void CButton::setImage(CAnimation* anim, bool playerColoredButton, int animFlags)
+void CButton::setImage(std::shared_ptr<CAnimation> anim, bool playerColoredButton, int animFlags)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
@@ -294,14 +295,9 @@ void CButton::setPlayerColor(PlayerColor player)
 void CButton::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
-	
-	#ifdef VCMI_SDL1
-	if (borderColor && borderColor->unused == 0)
-		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor->r, borderColor->g, borderColor->b));
-	#else
+
 	if (borderColor && borderColor->a == 0)
 		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor->r, borderColor->g, borderColor->b));
-	#endif // 0
 }
 
 std::pair<std::string, std::string> CButton::tooltip()
@@ -424,8 +420,8 @@ void CToggleGroup::addToggle(int identifier, CToggleBase* bt)
 	buttons[identifier] = bt;
 }
 
-CToggleGroup::CToggleGroup(const CFunctionList<void(int)> &OnChange, bool musicLikeButtons)
-: onChange(OnChange), selectedID(-2), musicLike(musicLikeButtons)
+CToggleGroup::CToggleGroup(const CFunctionList<void(int)> &OnChange)
+: onChange(OnChange), selectedID(-2)
 {}
 
 void CToggleGroup::setSelected(int id)
@@ -452,26 +448,86 @@ void CToggleGroup::selectionChanged(int to)
 		parent->redraw();
 }
 
-void CToggleGroup::show(SDL_Surface * to)
+CVolumeSlider::CVolumeSlider(const Point &position, const std::string &defName, const int value,
+                             const std::pair<std::string, std::string> * const help) :
+	value(value),
+	helpHandlers(help)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	animImage = new CAnimImage(std::make_shared<CAnimation>(defName), 0, 0, position.x, position.y),
+	assert(!defName.empty());
+	addUsedEvents(LCLICK | RCLICK | WHEEL);
+	pos.x += position.x;
+	pos.y += position.y;
+	pos.w = (animImage->pos.w + 1) * animImage->size();
+	pos.h = animImage->pos.h;
+	type |= REDRAW_PARENT;
+	setVolume(value);
+}
+
+void CVolumeSlider::setVolume(int value_)
 {
-	if (musicLike)
+	value = value_;
+	moveTo(value * static_cast<double>(animImage->size()) / 100.0);
+}
+
+void CVolumeSlider::moveTo(int id)
+{
+	vstd::abetween(id, 0, animImage->size() - 1);
+	animImage->setFrame(id);
+	animImage->moveTo(Point(pos.x + (animImage->pos.w + 1) * id, pos.y));
+	if (active)
+		redraw();
+}
+
+void CVolumeSlider::addCallback(std::function<void(int)> callback)
+{
+	onChange += callback;
+}
+
+void CVolumeSlider::clickLeft(tribool down, bool previousState)
+{
+	if (down)
 	{
-		if (auto intObj = dynamic_cast<CIntObject*>(buttons[selectedID])) // hack-ish workagound to avoid diamond problem with inheritance
-			intObj->show(to);
+		double px = GH.current->motion.x - pos.x;
+		double rx = px / static_cast<double>(pos.w);
+		// setVolume is out of 100
+		setVolume(rx * 100);
+		// Volume config is out of 100, set to increments of 5ish roughly based on the half point of the indicator
+		// 0.0 -> 0, 0.05 -> 5, 0.09 -> 5,...,
+		// 0.1 -> 10, ..., 0.19 -> 15, 0.2 -> 20, ...,
+		// 0.28 -> 25, 0.29 -> 30, 0.3 -> 30, ...,
+		// 0.85 -> 85, 0.86 -> 90, ..., 0.87 -> 90,...,
+		// 0.95 -> 95, 0.96 -> 100, 0.99 -> 100
+		int volume = 5 * int(rx * (2 * animImage->size() + 1));
+		onChange(volume);
 	}
-	else
-		CIntObject::show(to);
 }
 
-void CToggleGroup::showAll(SDL_Surface * to)
+void CVolumeSlider::clickRight(tribool down, bool previousState)
 {
-	if (musicLike)
+	if (down)
 	{
-		if (auto intObj = dynamic_cast<CIntObject*>(buttons[selectedID])) // hack-ish workagound to avoid diamond problem with inheritance
-			intObj->showAll(to);
+		double px = GH.current->motion.x - pos.x;
+		int index = px / static_cast<double>(pos.w) * animImage->size();
+		std::string hoverText = helpHandlers[index].first;
+		std::string helpBox = helpHandlers[index].second;
+		if(!helpBox.empty())
+			CRClickPopup::createAndPush(helpBox);
+		if(GH.statusbar)
+			GH.statusbar->setText(helpBox);
+	}
+}
+
+void CVolumeSlider::wheelScrolled(bool down, bool in)
+{
+	if (in)
+	{
+		int volume = value + 3 * (down ? 1 : -1);
+		vstd::abetween(volume, 0, 100);
+		setVolume(volume);
+		onChange(volume);
 	}
-	else
-		CIntObject::showAll(to);
 }
 
 void CSlider::sliderClicked()

+ 39 - 20
client/widgets/Buttons.h

@@ -10,7 +10,6 @@ struct Rect;
 class CAnimImage;
 class CLabel;
 class CAnimation;
-class CDefHandler;
 
 namespace config
 {
@@ -111,7 +110,7 @@ public:
 
 	/// Appearance modifiers
 	void setIndex(size_t index, bool playerColoredButton=false);
-	void setImage(CAnimation* anim, bool playerColoredButton=false, int animFlags=0);
+	void setImage(std::shared_ptr<CAnimation> anim, bool playerColoredButton=false, int animFlags=0);
 	void setPlayerColor(PlayerColor player);
 
 	/// CIntObject overrides
@@ -178,12 +177,11 @@ class CToggleGroup : public CIntObject
 	CFunctionList<void(int)> onChange; //called when changing selected button with new button's id
 
 	int selectedID;
-	bool musicLike; //determines the behaviour of this group
 	void selectionChanged(int to);
 public:
 	std::map<int, CToggleBase*> buttons;
 
-	CToggleGroup(const CFunctionList<void(int)> & OnChange, bool musicLikeButtons = false);
+	CToggleGroup(const CFunctionList<void(int)> & OnChange);
 
 	void addCallback(std::function<void(int)> callback);
 
@@ -191,9 +189,32 @@ public:
 	void addToggle(int index, CToggleBase * button);
 	/// Changes selection to specific value. Will select toggle with this ID, if present
 	void setSelected(int id);
+};
+
+/// A typical slider for volume with an animated indicator
+class CVolumeSlider : public CIntObject
+{
+	int value;
+	CFunctionList<void(int)> onChange;
+	CAnimImage * animImage;
+	const std::pair<std::string, std::string> * const helpHandlers;
+	void setVolume(const int v);
+public:
+
+	/// @param position coordinates of slider
+	/// @param defName name of def animation for slider
+	/// @param value initial value for volume
+	/// @param help pointer to first helptext of slider
+	CVolumeSlider(const Point &position, const std::string &defName, const int value,
+	              const std::pair<std::string, std::string> * const help);
+
+	void moveTo(int id);
+	void addCallback(std::function<void(int)> callback);
 
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
+	void wheelScrolled(bool down, bool in) override;
 };
 
 /// A typical slider which can be orientated horizontally/vertically.
@@ -242,20 +263,18 @@ public:
 
 	void addCallback(std::function<void(int)> callback);
 
-	void keyPressed(const SDL_KeyboardEvent & key);
-	void wheelScrolled(bool down, bool in);
-	void clickLeft(tribool down, bool previousState);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
-	void showAll(SDL_Surface * to);	
-
-	/**
-	 * @param position, coordinates of slider
-	 * @param length, length of slider ribbon, including left/right buttons
-	 * @param Moved, function that will be called whenever slider moves
-	 * @param Capacity, maximal number of visible at once elements
-	 * @param Amount, total amount of elements, including not visible
-	 * @param Value, starting position
-	 */
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	void wheelScrolled(bool down, bool in) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
+	void showAll(SDL_Surface * to) override;
+
+	 /// @param position coordinates of slider
+	 /// @param length length of slider ribbon, including left/right buttons
+	 /// @param Moved function that will be called whenever slider moves
+	 /// @param Capacity maximal number of visible at once elements
+	 /// @param Amount total amount of elements, including not visible
+	 /// @param Value starting position
 	CSlider(Point position, int length, std::function<void(int)> Moved, int Capacity, int Amount,
 		int Value=0, bool Horizontal=true, EStyle style = BROWN);
 	~CSlider();

+ 66 - 87
client/widgets/CArtifactHolder.cpp

@@ -32,7 +32,7 @@
  */
 
 CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art):
-    locked(false), picked(false), marked(false), ourArt(Art)
+	locked(false), picked(false), marked(false), ourArt(Art)
 {
 	pos += position;
 	pos.w = pos.h = 44;
@@ -123,10 +123,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 	if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
 	{
 		if(ourArt->artType->id == ArtifactID::SPELLBOOK)
-		{
-			auto   spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt);
-			GH.pushInt(spellWindow);
-		}
+			GH.pushInt(new CSpellWindow(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt));
 	}
 
 	if (!down && previousState)
@@ -180,7 +177,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 						if(srcInBackpack && srcInSameHero)
 						{
 							if(!ourArt								//cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact
-							  || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
+								|| ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
 								vstd::advance(ourOwner->commonInfo->dst.slotID, -1);
 						}
 						if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst
@@ -214,6 +211,34 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 	}
 }
 
+bool CArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
+                              const CGHeroInstance *hero)
+{
+	assert(art != nullptr);
+	assert(hero != nullptr);
+	std::vector<const CArtifact *> assemblyPossibilities = art->assemblyPossibilities(hero);
+
+	// If the artifact can be assembled, display dialog.
+	for(const CArtifact *combination : assemblyPossibilities)
+	{
+		LOCPLINT->showArtifactAssemblyDialog(
+			art->artType->id,
+			combination->id,
+			true,
+			std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id),
+			0);
+
+		if(assemblyPossibilities.size() > 2)
+		{
+			logGlobal->warnStream() << boost::format(
+				"More than one possibility of assembling on %s... taking only first")
+				% art->artType->Name();
+		}
+		return true;
+	}
+	return false;
+}
+
 void CArtPlace::clickRight(tribool down, bool previousState)
 {
 	if(down && ourArt && !locked && text.size() && !picked)  //if there is no description or it's a lock, do nothing ;]
@@ -225,20 +250,8 @@ void CArtPlace::clickRight(tribool down, bool previousState)
 				std::vector<const CArtifact *> assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero);
 
 				// If the artifact can be assembled, display dialog.
-				for(const CArtifact *combination : assemblyPossibilities)
+				if (askToAssemble(ourArt, slotID, ourOwner->curHero))
 				{
-					LOCPLINT->showArtifactAssemblyDialog(
-						ourArt->artType->id,
-						combination->id,
-						true,
-						std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, true, combination->id),
-						0);
-
-					if(assemblyPossibilities.size() > 2)
-					{
-                        logGlobal->warnStream() << "More than one possibility of assembling... taking only first";
-						break;
-					}
 					return;
 				}
 
@@ -303,10 +316,10 @@ void CArtPlace::deselect ()
 		for(int i = 0; i < GameConstants::BACKPACK_START; i++)
 		{
 			auto place = ourOwner->getArtPlace(i);
-			
+
 			if(nullptr != place)//getArtPlace may return null
 				place->pickSlot(false);
-		}			
+		}
 	}
 
 	CCS->curh->dragAndDropCursor(nullptr);
@@ -372,70 +385,36 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
 		image->disable();
 		text = std::string();
 		hoverText = CGI->generaltexth->allTexts[507];
+		return;
 	}
-	else
-	{
-		image->enable();
-		image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
 
-		std::string artDesc = ourArt->artType->Description();
-		if (vstd::contains (artDesc, '{'))
-			text = artDesc;
-		else
-			text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style
+	image->enable();
+	image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
 
-		if(art->artType->id == ArtifactID::SPELL_SCROLL)
-		{
-			// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
-			// so we want to replace text in [...] with a spell name
-			// however other language versions don't have name placeholder at all, so we have to be careful
-			int spellID = art->getGivenSpellID();
-			size_t nameStart = text.find_first_of('[');
-			size_t nameEnd = text.find_first_of(']', nameStart);
-			if(spellID >= 0)
-			{
-				if(nameStart != std::string::npos  &&  nameEnd != std::string::npos)
-					text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name);
+	text = art->getEffectiveDescription(ourOwner->curHero);
 
-				//add spell component info (used to provide a pic in r-click popup)
-				baseType = CComponent::spell;
-				type = spellID;
-				bonusValue = 0;
-			}
-		}
-		else
+	if(art->artType->id == ArtifactID::SPELL_SCROLL)
+	{
+		int spellID = art->getGivenSpellID();
+		if(spellID >= 0)
 		{
-			baseType = CComponent::artifact;
-			type = art->artType->id;
+			//add spell component info (used to provide a pic in r-click popup)
+			baseType = CComponent::spell;
+			type = spellID;
 			bonusValue = 0;
 		}
-		if (art->artType->constituents) //display info about components of combined artifact
-		{
-			//TODO
-		}
-		else if (art->artType->constituentOf.size()) //display info about set
-		{
-			std::string artList;
-			auto combinedArt = art->artType->constituentOf[0];
-			text += "\n\n";
-			text += "{" + combinedArt->Name() + "}";
-			int wornArtifacts = 0;
-			for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
-			{
-				artList += "\n" + a->Name();
-				if (ourOwner->curHero->hasArt(a->id, true))
-					wornArtifacts++;
-			}
-			text += " (" + boost::str(boost::format("%d") % wornArtifacts) +  " / " +
-				boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;
-			//TODO: fancy colors and fonts for this text
-		}
-
-		if (locked) // Locks should appear as empty.
-			hoverText = CGI->generaltexth->allTexts[507];
-		else
-			hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
 	}
+	else
+	{
+		baseType = CComponent::artifact;
+		type = art->artType->id;
+		bonusValue = 0;
+	}
+
+	if (locked) // Locks should appear as empty.
+		hoverText = CGI->generaltexth->allTexts[507];
+	else
+		hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
 }
 
 void CArtifactsOfHero::SCommonPart::reset()
@@ -454,12 +433,12 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
 		backpackPos = 0;
 
 	// Fill the slots for worn artifacts and backpack.
-	
+
 	for(auto p : artWorn)
 	{
 		setSlotData(p.second, p.first);
 	}
-	
+
 	scrollBackpack(0);
 }
 
@@ -587,8 +566,8 @@ void CArtifactsOfHero::setSlotData(CArtPlace* artPlace, ArtifactPosition slotID)
 
 	if(const ArtSlotInfo *asi = curHero->getSlot(slotID))
 	{
-		artPlace->setArtifact(asi->artifact);
 		artPlace->lockSlot(asi->locked);
+		artPlace->setArtifact(asi->artifact);
 	}
 	else
 		artPlace->setArtifact(nullptr);
@@ -615,7 +594,7 @@ CArtifactsOfHero::CArtifactsOfHero(std::map<ArtifactPosition, CArtPlace *> ArtWo
 {
 	if(createCommonPart)
 	{
-		commonInfo = new CArtifactsOfHero::SCommonPart;
+		commonInfo = std::make_shared<CArtifactsOfHero::SCommonPart>();
 		commonInfo->participants.insert(this);
 	}
 
@@ -642,7 +621,7 @@ CArtifactsOfHero::CArtifactsOfHero(const Point& position, bool createCommonPart
 {
 	if(createCommonPart)
 	{
-		commonInfo = new CArtifactsOfHero::SCommonPart;
+		commonInfo = std::make_shared<CArtifactsOfHero::SCommonPart>();
 		commonInfo->participants.insert(this);
 	}
 
@@ -797,7 +776,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
 	}
 	else if(src.slot >= GameConstants::BACKPACK_START &&
 	        src.slot <  commonInfo->src.slotID &&
-			src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
+			    src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
 	{
 		//int fixedSlot = src.hero->getArtPos(commonInfo->src.art);
 		vstd::advance(commonInfo->src.slotID, -1);
@@ -811,14 +790,14 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
 	}
 
 	updateParentWindow();
- 	int shift = 0;
+	int shift = 0;
 // 	if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos)
 // 		shift++;
 //
- 	if(src.slot < GameConstants::BACKPACK_START  &&  dst.slot - GameConstants::BACKPACK_START < backpackPos)
+	if(src.slot < GameConstants::BACKPACK_START  &&  dst.slot - GameConstants::BACKPACK_START < backpackPos)
 		shift++;
 	if(dst.slot < GameConstants::BACKPACK_START  &&  src.slot - GameConstants::BACKPACK_START < backpackPos)
- 		shift--;
+		shift--;
 
 	if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
 	 || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) )
@@ -853,7 +832,7 @@ CArtPlace * CArtifactsOfHero::getArtPlace(int slot)
 		for(CArtPlace *ap : backpack)
 			if(ap->slotID == slot)
 				return ap;
-		return nullptr;				
+		return nullptr;
 	}
 }
 

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.