浏览代码

Merge branch 'develop' into mutexRelax

DjWarmonger 10 年之前
父节点
当前提交
a6ea0981b6
共有 100 个文件被更改,包括 1539 次插入1118 次删除
  1. 1 1
      AI/BattleAI/BattleAI.cpp
  2. 2 2
      AI/VCAI/AIUtility.cpp
  3. 21 21
      CCallback.h
  4. 2 2
      CMakeLists.txt
  5. 1 1
      ChangeLog
  6. 2 1
      Global.h
  7. 3 2
      client/CMakeLists.txt
  8. 1 1
      client/CMessage.cpp
  9. 6 6
      client/CMusicHandler.h
  10. 14 7
      client/CPlayerInterface.cpp
  11. 6 6
      client/CPlayerInterface.h
  12. 2 2
      client/CPreGame.cpp
  13. 29 29
      client/CPreGame.h
  14. 10 7
      client/Client.cpp
  15. 1 1
      client/Client.h
  16. 1 1
      client/NetPacksClient.cpp
  17. 1 1
      client/battle/CBattleAnimations.cpp
  18. 25 25
      client/battle/CBattleAnimations.h
  19. 27 12
      client/battle/CBattleInterface.cpp
  20. 8 8
      client/battle/CBattleInterface.h
  21. 11 11
      client/battle/CBattleInterfaceClasses.h
  22. 8 8
      client/gui/CAnimation.h
  23. 6 6
      client/gui/CIntObject.h
  24. 1 1
      client/gui/SDL_Extensions.h
  25. 1 1
      client/mapHandler.h
  26. 28 28
      client/widgets/AdventureMapClasses.h
  27. 6 6
      client/widgets/Buttons.h
  28. 7 7
      client/widgets/CArtifactHolder.h
  29. 3 3
      client/widgets/CComponent.h
  30. 21 18
      client/widgets/CGarrisonInt.cpp
  31. 4 4
      client/widgets/CGarrisonInt.h
  32. 7 7
      client/widgets/Images.h
  33. 13 13
      client/widgets/MiscWidgets.h
  34. 4 4
      client/widgets/TextControls.h
  35. 16 16
      client/windows/CAdvmapInterface.h
  36. 1 1
      client/windows/CCastleInterface.cpp
  37. 28 28
      client/windows/CCastleInterface.h
  38. 2 2
      client/windows/CHeroWindow.h
  39. 34 34
      client/windows/CKingdomInterface.h
  40. 8 8
      client/windows/CQuestLog.h
  41. 18 18
      client/windows/CTradeWindow.h
  42. 2 2
      client/windows/CWindowObject.h
  43. 20 20
      client/windows/GUIClasses.h
  44. 8 8
      client/windows/InfoWindows.h
  45. 12 3
      config/artifacts.json
  46. 5 0
      config/bonuses.json
  47. 75 69
      config/bonuses_texts.json
  48. 8 4
      config/creatures/dungeon.json
  49. 9 0
      config/creatures/neutral.json
  50. 1 1
      config/spells/timed.json
  51. 41 1
      lib/BattleState.cpp
  52. 16 0
      lib/BattleState.h
  53. 9 12
      lib/CArtHandler.cpp
  54. 1 1
      lib/CArtHandler.h
  55. 38 42
      lib/CBattleCallback.cpp
  56. 8 8
      lib/CBattleCallback.h
  57. 8 0
      lib/CCreatureHandler.cpp
  58. 1 1
      lib/CCreatureHandler.h
  59. 4 4
      lib/CCreatureSet.h
  60. 1 1
      lib/CGameInfoCallback.cpp
  61. 21 21
      lib/CGameInterface.h
  62. 1 1
      lib/CGameState.cpp
  63. 1 1
      lib/CGeneralTextHandler.cpp
  64. 3 3
      lib/CHeroHandler.h
  65. 3 3
      lib/Connection.h
  66. 6 2
      lib/GameConstants.h
  67. 1 1
      lib/HeroBonus.cpp
  68. 8 6
      lib/HeroBonus.h
  69. 6 6
      lib/NetPacks.h
  70. 1 1
      lib/NetPacksBase.h
  71. 15 23
      lib/NetPacksLib.cpp
  72. 3 0
      lib/VCMI_lib.cbp
  73. 1 1
      lib/filesystem/AdapterLoaders.h
  74. 1 1
      lib/filesystem/CArchiveLoader.h
  75. 1 1
      lib/filesystem/CCompressedStream.h
  76. 43 23
      lib/mapObjects/CGHeroInstance.cpp
  77. 19 5
      lib/mapObjects/CGHeroInstance.h
  78. 1 1
      lib/mapObjects/CGMarket.h
  79. 1 1
      lib/mapObjects/CGTownInstance.cpp
  80. 7 7
      lib/mapObjects/CGTownInstance.h
  81. 3 3
      lib/mapObjects/CQuest.h
  82. 4 6
      lib/mapObjects/CommonConstructors.cpp
  83. 28 28
      lib/mapObjects/CommonConstructors.h
  84. 2 2
      lib/mapObjects/MiscObjects.h
  85. 2 2
      lib/mapping/MapFormatH3M.h
  86. 3 3
      lib/mapping/MapFormatJson.h
  87. 2 2
      lib/rmg/CMapGenerator.cpp
  88. 4 4
      lib/rmg/CRmgTemplateStorage.h
  89. 10 2
      lib/rmg/CRmgTemplateZone.cpp
  90. 202 77
      lib/spells/BattleSpellMechanics.cpp
  91. 40 20
      lib/spells/BattleSpellMechanics.h
  92. 161 173
      lib/spells/CDefaultSpellMechanics.cpp
  93. 11 10
      lib/spells/CDefaultSpellMechanics.h
  94. 87 70
      lib/spells/CSpellHandler.cpp
  95. 21 55
      lib/spells/CSpellHandler.h
  96. 8 7
      lib/spells/CreatureSpellMechanics.cpp
  97. 3 3
      lib/spells/CreatureSpellMechanics.h
  98. 59 0
      lib/spells/ISpellMechanics.cpp
  99. 71 4
      lib/spells/ISpellMechanics.h
  100. 18 2
      lib/spells/Magic.h

+ 1 - 1
AI/BattleAI/BattleAI.cpp

@@ -531,7 +531,7 @@ std::vector<BattleHex> CBattleAI::getTargetsToConsider( const CSpell *spell ) co
 {
 	if(spell->getTargetType() == CSpell::NO_TARGET)
 	{
-		//Spell can be casted anywhere, all hexes are potentially considerable.
+		//Spell can be cast anywhere, all hexes are potentially considerable.
 		std::vector<BattleHex> ret;
 
 		for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)

+ 2 - 2
AI/VCAI/AIUtility.cpp

@@ -432,7 +432,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;
@@ -509,4 +509,4 @@ bool compareArtifacts(const CArtifactInstance *a1, const CArtifactInstance *a2)
 		return true;
 	else
 		return art1->price > art2->price;
-}
+}

+ 21 - 21
CCallback.h

@@ -119,34 +119,34 @@ public:
 	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;

+ 2 - 2
CMakeLists.txt

@@ -115,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)
@@ -150,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

+ 1 - 1
ChangeLog

@@ -3,7 +3,7 @@
 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
 
 RANDOM MAP GENERATOR:
 * Changed fractalization algorithm so it can create cycles

+ 2 - 1
Global.h

@@ -61,7 +61,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #  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
@@ -150,6 +150,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>

+ 3 - 2
client/CMakeLists.txt

@@ -102,7 +102,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,7 +110,8 @@ 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})

+ 1 - 1
client/CMessage.cpp

@@ -45,7 +45,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

+ 6 - 6
client/CMusicHandler.h

@@ -52,10 +52,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);
@@ -127,9 +127,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);

+ 14 - 7
client/CPlayerInterface.cpp

@@ -772,7 +772,8 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
 {
 	THREAD_CREATED_BY_CLIENT;
 	logGlobal->traceStream() << "Awaiting command for " << stack->nodeName();
-
+	auto stackId = stack->ID;
+	auto stackName = stack->nodeName();
 	if(autofightingAI)
 	{
 		if(isAutoFightOn)
@@ -806,17 +807,23 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
 	while(!b->givenCommand->data)
 	{
 		b->givenCommand->cond.wait(lock);
-		if(!battleInt) //batle ended while we were waiting for movement (eg. because of spell)
+		if(!battleInt) //battle ended while we were waiting for movement (eg. because of spell)
 			throw boost::thread_interrupted(); //will shut the thread peacefully
 	}
 
 	//tidy up
 	BattleAction ret = *(b->givenCommand->data);
-	delete b->givenCommand->data;
-	b->givenCommand->data = nullptr;
-
-	//return command
-	logGlobal->traceStream() << "Giving command for " << stack->nodeName();
+	vstd::clear_pointer(b->givenCommand->data);
+	
+	if(ret.actionType == Battle::CANCEL)
+	{
+		if(stackId != ret.stackNumber)
+			logGlobal->error("Not current active stack action canceled");
+		logGlobal->traceStream() << "Canceled command for " << stackName;			
+	}
+	else
+		logGlobal->traceStream() << "Giving command for " << stackName;
+		
 	return ret;
 }
 

+ 6 - 6
client/CPlayerInterface.h

@@ -147,11 +147,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;
@@ -236,7 +236,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(shared_ptr<CCallback> CB) override;
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
 
 	// show dialogs

+ 2 - 2
client/CPreGame.cpp

@@ -207,7 +207,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);
@@ -217,7 +217,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);

+ 29 - 29
client/CPreGame.h

@@ -75,10 +75,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 +100,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,7 +115,7 @@ public:
 
 	CChatBox(const Rect &rect);
 
-	void keyPressed(const SDL_KeyboardEvent & key);
+	void keyPressed(const SDL_KeyboardEvent & key) override;
 
 	void addNewMessage(const std::string &text);
 };
@@ -136,8 +136,8 @@ public:
 	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);
@@ -184,10 +184,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 +235,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 +254,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 +282,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,7 +295,7 @@ 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;
@@ -385,7 +385,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 +454,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
@@ -498,9 +498,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();
@@ -560,12 +560,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 +578,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,...
@@ -629,7 +629,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.

+ 10 - 7
client/Client.cpp

@@ -72,12 +72,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 +87,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,9 +140,12 @@ 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&)

+ 1 - 1
client/Client.h

@@ -189,7 +189,7 @@ public:
 	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 {}

+ 1 - 1
client/NetPacksClient.cpp

@@ -750,7 +750,7 @@ void CatapultAttack::applyCl( CClient *cl )
 	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);

+ 1 - 1
client/battle/CBattleAnimations.cpp

@@ -782,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);

+ 27 - 12
client/battle/CBattleInterface.cpp

@@ -1005,11 +1005,22 @@ void CBattleInterface::newStack(const CStack * stack)
 
 void CBattleInterface::stackRemoved(int stackID)
 {
+	if(activeStack != nullptr)
+	{
+		if(activeStack->ID == stackID)
+		{
+			BattleAction * action = new BattleAction();
+			action->side = defendingHeroInstance ? (curInt->playerID == defendingHeroInstance->tempOwner) : false;
+			action->actionType = Battle::CANCEL;
+			action->stackNumber = activeStack->ID;
+			givenCommand->setn(action);
+			setActiveStack(nullptr);
+		}
+	}
+	
 	delete creAnims[stackID];
 	creAnims.erase(stackID);
 	creDir.erase(stackID);
-	//FIXME: what if currently removed stack is active one (Sacrifice)?
-
 	redrawBackgroundWithHexes(activeStack);
 	queue->update();
 }
@@ -1091,10 +1102,6 @@ void CBattleInterface::newRoundFirst( int round )
 void CBattleInterface::newRound(int number)
 {
 	console->addText(CGI->generaltexth->allTexts[412]);
-
-	//unlock spellbook
-	//bSpell->block(!curInt->cb->battleCanCastSpell());
-	//don't unlock spellbook - this should be done when we have axctive creature
 }
 
 void CBattleInterface::giveCommand(Battle::ActionType action, BattleHex tile, ui32 stackID, si32 additional, si32 selected)
@@ -1379,7 +1386,7 @@ void CBattleInterface::castThisSpell(SpellID spellID)
 	sp = spellID.toSpell();
 	spellSelMode = ANY_LOCATION;
 
-	const CSpell::TargetInfo ti = sp->getTargetInfo(castingHero->getSpellSchoolLevel(sp));
+	const CSpell::TargetInfo ti(sp, castingHero->getSpellSchoolLevel(sp));
 
 	if(ti.massive || ti.type == CSpell::NO_TARGET)
 		spellSelMode = NO_LOCATION;
@@ -2103,9 +2110,9 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 			{
 				ui8 skill = 0;
 				if (creatureCasting)
-					skill = sactive->getSpellSchoolLevel(SpellID(SpellID::TELEPORT).toSpell());
+					skill = sactive->getEffectLevel(SpellID(SpellID::TELEPORT).toSpell());
 				else
-					skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->objects[spellToCast->additionalInfo]);
+					skill = getActiveHero()->getEffectLevel(SpellID(SpellID::TELEPORT).toSpell());
 				//TODO: explicitely save power, skill
 				if (curInt->cb->battleCanTeleportTo(selectedStack, myNumber, skill))
 					legalAction = true;
@@ -2470,11 +2477,19 @@ bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CSta
 
 	if (sp)
 	{
-		if (creatureCasting)
-			isCastingPossible = (curInt->cb->battleCanCreatureCastThisSpell (sp, myNumber) == ESpellCastProblem::OK);
+		const ISpellCaster * caster = creatureCasting ? dynamic_cast<const ISpellCaster *>(sactive) : dynamic_cast<const ISpellCaster *>(curInt->cb->battleGetMyHero());
+		if(caster == nullptr)
+		{
+			isCastingPossible = false;//just in case
+		}
 		else
-			isCastingPossible = (curInt->cb->battleCanCastThisSpell (sp, myNumber) == ESpellCastProblem::OK);
+		{
+			const ECastingMode::ECastingMode mode = creatureCasting ? ECastingMode::CREATURE_ACTIVE_CASTING : ECastingMode::HERO_CASTING;
+			isCastingPossible = (curInt->cb->battleCanCastThisSpellHere(caster, sp, mode, myNumber) == ESpellCastProblem::OK);
+		}
 	}
+	else
+		isCastingPossible = false;
 	if(!myNumber.isAvailable() && !shere) //empty tile outside battlefield (or in the unavailable border column)
 			isCastingPossible = false;
 

+ 8 - 8
client/battle/CBattleInterface.h

@@ -291,14 +291,14 @@ 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 show(SDL_Surface *to);
-	void showAll(SDL_Surface *to);
+	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) override;
+	void showAll(SDL_Surface *to) override;
 
 	//call-ins
 	void startAction(const BattleAction* action);

+ 11 - 11
client/battle/CBattleInterfaceClasses.h

@@ -38,7 +38,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,9 +60,9 @@ 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 clickLeft(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
 };
@@ -96,8 +96,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 +114,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 +132,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 +150,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 - 8
client/gui/CAnimation.h

@@ -98,10 +98,10 @@ 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 +144,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;
 };

+ 6 - 6
client/gui/CIntObject.h

@@ -157,15 +157,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};
 
@@ -212,5 +212,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
 };

+ 1 - 1
client/gui/SDL_Extensions.h

@@ -113,7 +113,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";

+ 1 - 1
client/mapHandler.h

@@ -312,7 +312,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;
 

+ 28 - 28
client/widgets/AdventureMapClasses.h

@@ -37,9 +37,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 +118,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 +152,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 +195,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 +213,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 +232,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 +256,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 +283,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 +331,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
@@ -351,7 +351,7 @@ public:
 	void addChildIcon(std::pair<int, Point> data, const CDefHandler *def, 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 showAll(SDL_Surface * to) override;
 };
 
 class CInGameConsole : public CIntObject
@@ -365,9 +365,9 @@ 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
 
 	void textInputed(const SDL_TextInputEvent & event) override;
 	void textEdited(const SDL_TextEditingEvent & event) override;

+ 6 - 6
client/widgets/Buttons.h

@@ -215,7 +215,7 @@ public:
 
 	void clickLeft(tribool down, bool previousState) override;
 	void clickRight(tribool down, bool previousState) override;
-	void wheelScrolled(bool down, bool in);
+	void wheelScrolled(bool down, bool in) override;
 };
 
 /// A typical slider which can be orientated horizontally/vertically.
@@ -264,11 +264,11 @@ 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);	
+	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

+ 7 - 7
client/widgets/CArtifactHolder.h

@@ -35,10 +35,10 @@ class CWindowWithArtifacts : public CArtifactHolder
 public:
 	std::vector<CArtifactsOfHero *> artSets;
 
-	void artifactRemoved(const ArtifactLocation &artLoc);
-	void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc);
-	void artifactDisassembled(const ArtifactLocation &artLoc);
-	void artifactAssembled(const ArtifactLocation &artLoc);
+	void artifactRemoved(const ArtifactLocation &artLoc) override;
+	void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc) override;
+	void artifactDisassembled(const ArtifactLocation &artLoc) override;
+	void artifactAssembled(const ArtifactLocation &artLoc) override;
 };
 
 /// Artifacts can be placed there. Gets shown at the hero window
@@ -65,11 +65,11 @@ public:
 	const CArtifactInstance * ourArt; // should be changed only with setArtifact()
 
 	CArtPlace(Point position, const CArtifactInstance * Art = nullptr); //c-tor
-	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;
 	void select ();
 	void deselect ();
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 	bool fitsHere (const CArtifactInstance * art) const; //returns true if given artifact can be placed here
 
 	void setMeAsDest(bool backpackAsVoid = true);

+ 3 - 3
client/widgets/CComponent.h

@@ -57,7 +57,7 @@ public:
 	CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large);//c-tor
 	CComponent(const Component &c, ESize imageSize=large); //c-tor
 
-	void clickRight(tribool down, bool previousState); //call-in
+	void clickRight(tribool down, bool previousState) override; //call-in
 };
 
 /// component that can be selected or deselected
@@ -68,10 +68,10 @@ public:
 	bool selected; //if true, this component is selected
 	std::function<void()> onSelect; //function called on selection change
 
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 	void select(bool on);
 
-	void clickLeft(tribool down, bool previousState); //call-in
+	void clickLeft(tribool down, bool previousState) override; //call-in
 	CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize=large, std::function<void()> OnSelect = nullptr); //c-tor
 	CSelectableComponent(const Component &c, std::function<void()> OnSelect = nullptr); //c-tor
 };

+ 21 - 18
client/widgets/CGarrisonInt.cpp

@@ -293,27 +293,30 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 			redraw();
 			refr = true;
 		}
-		// we want to split
-		else if(  (owner->getSplittingMode() || LOCPLINT->shiftPressed())
-		       && (!creature || creature == selection->creature) )
-			refr = split();
-		// swap
-		else if(creature != selection->creature)
-		{
-			const CArmedInstance * selectedObj = owner->armedObjs[selection->upg];
-			if (!creature && selectedObj->stacksCount() == 1)
-				LOCPLINT->cb->splitStack(selectedObj, owner->armedObjs[upg], selection->ID, ID, selection->myStack->count - 1);
-			else
-				LOCPLINT->cb->swapCreatures(owner->armedObjs[upg], owner->armedObjs[selection->upg], ID, selection->ID);
-		}
-		// merge
 		else
 		{
 			const CArmedInstance * selectedObj = owner->armedObjs[selection->upg];
-			if (selectedObj->stacksCount() == 1)
-				LOCPLINT->cb->splitStack(owner->armedObjs[upg], selectedObj, selection->ID, ID, 1);
-			else
-				LOCPLINT->cb->mergeStacks(owner->armedObjs[selection->upg], owner->armedObjs[upg], selection->ID, ID);
+			bool lastHeroStackSelected = false;
+			if(selectedObj->stacksCount() == 1
+				&& owner->getSelection()->upg != upg
+				&& dynamic_cast<const CGHeroInstance*>(selectedObj))
+			{
+				lastHeroStackSelected = true;
+			}
+
+			if((owner->getSplittingMode() || LOCPLINT->shiftPressed()) // split window
+				&& (!creature || creature == selection->creature))
+			{
+				refr = split();
+			}
+			else if(!creature && lastHeroStackSelected) // split all except last creature
+				LOCPLINT->cb->splitStack(selectedObj, owner->armedObjs[upg], selection->ID, ID, selection->myStack->count - 1);
+			else if(creature != selection->creature) // swap
+				LOCPLINT->cb->swapCreatures(owner->armedObjs[upg], selectedObj, ID, selection->ID);
+			else if(lastHeroStackSelected) // merge last stack to other hero stack
+				refr = split();
+			else // merge
+				LOCPLINT->cb->mergeStacks(selectedObj, owner->armedObjs[upg], selection->ID, ID);
 		}
 		if(refr)
 		{

+ 4 - 4
client/widgets/CGarrisonInt.h

@@ -47,12 +47,12 @@ class CGarrisonSlot : public CIntObject
 
 	void setHighlight(bool on);
 public:
-	virtual void hover (bool on); //call-in
+	virtual void hover (bool on) override; //call-in
 	const CArmedInstance * getObj() const;
 	bool our() const;
 	bool ally() const;
-	void clickRight(tribool down, bool previousState);
-	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState) override;
+	void clickLeft(tribool down, bool previousState) override;
 	void update();
 	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, EGarrisonType Upg=EGarrisonType::UP, const CStackInstance * Creature=nullptr);
 
@@ -132,7 +132,7 @@ class CWindowWithGarrison : public virtual CGarrisonHolder
 {
 public:
 	CGarrisonInt *garr;
-	virtual void updateGarrisons();
+	virtual void updateGarrisons() override;
 };
 
 /// Garrison window where you can take creatures out of the hero to place it on the garrison

+ 7 - 7
client/widgets/Images.h

@@ -49,8 +49,8 @@ public:
 
 	void scaleTo(Point size);
 	void createSimpleRect(const Rect &r, bool screenFormat, ui32 color);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 	void convertToScreenBPP();
 	void colorizeAndConvert(PlayerColor player);
 	void colorize(PlayerColor player);
@@ -64,7 +64,7 @@ class CFilledTexture : CIntObject
 public:
 	CFilledTexture(std::string imageName, Rect position);
 	~CFilledTexture();
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 };
 
 /// Class for displaying one image from animation
@@ -94,7 +94,7 @@ public:
 	//makes image player-colored
 	void playerColored(PlayerColor player);
 
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 };
 
 /// Base class for displaying animation, used as superclass for different animations
@@ -155,8 +155,8 @@ public:
 	virtual void reset();
 
 	//show current frame and increase counter
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 };
 
 /// Creature-dependend animations like attacking, moving,...
@@ -210,7 +210,7 @@ private:
 
 public:
 	//change anim to next if queue is not empty, call callback othervice
-	void reset();
+	void reset() override;
 
 	//add sequence to the end of queue
 	void addLast(EAnimType newType);

+ 13 - 13
client/widgets/MiscWidgets.h

@@ -28,7 +28,7 @@ class CHoverableArea: public virtual CIntObject
 public:
 	std::string hoverText;
 
-	virtual void hover (bool on);
+	virtual void hover (bool on) override;
 
 	CHoverableArea();
 	virtual ~CHoverableArea();
@@ -45,8 +45,8 @@ public:
 	virtual ~LRClickableAreaWText();
 	void init();
 
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState);
+	virtual void clickLeft(tribool down, bool previousState) override;
+	virtual void clickRight(tribool down, bool previousState) override;
 };
 
 /// base class for hero/town/garrison tooltips
@@ -88,7 +88,7 @@ private:
 	CCreatureAnim *anim; //displayed animation
 	CLabel * amount;
 
-	void show(SDL_Surface *to);
+	void show(SDL_Surface *to) override;
 public:
 	CCreaturePic(int x, int y, const CCreature *cre, bool Big=true, bool Animated=true); //c-tor
 
@@ -100,8 +100,8 @@ class CMinorResDataBar : public CIntObject
 {
 public:
 	SDL_Surface *bg; //background bitmap
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 	CMinorResDataBar(); //c-tor
 	~CMinorResDataBar(); //d-tor
 };
@@ -114,9 +114,9 @@ public:
 
 	CHeroArea(int x, int y, const CGHeroInstance * _hero);
 
-	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;
 };
 
 /// Can interact on left and right mouse clicks
@@ -125,8 +125,8 @@ class LRClickableAreaWTextComp: public LRClickableAreaWText
 public:
 	int baseType;
 	int bonusValue, type;
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState);
+	virtual void clickLeft(tribool down, bool previousState) override;
+	virtual void clickRight(tribool down, bool previousState) override;
 
 	LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
 	CComponent * createComponent() const;
@@ -137,8 +137,8 @@ class LRClickableAreaOpenTown: public LRClickableAreaWTextComp
 {
 public:
 	const CGTownInstance * town;
-	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;
 	LRClickableAreaOpenTown();
 };
 

+ 4 - 4
client/widgets/TextControls.h

@@ -52,7 +52,7 @@ public:
 
 	CLabel(int x=0, int y=0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT,
 	       const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
-	void showAll(SDL_Surface * to); //shows statusbar (with current text)
+	void showAll(SDL_Surface * to) override; //shows statusbar (with current text)
 };
 
 /// Small helper class to manage group of similar labels
@@ -85,8 +85,8 @@ public:
 
 	CMultiLineLabel(Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
 
-	void setText(const std::string &Txt);
-	void showAll(SDL_Surface * to);
+	void setText(const std::string &Txt) override;
+	void showAll(SDL_Surface * to) override;
 
 	void setVisibleSize(Rect visibleSize);
 	// scrolls text visible in widget. Positive value will move text up
@@ -125,7 +125,7 @@ public:
 	void clear();//clears statusbar and refreshes
 	void setText(const std::string & Text) override; //prints text and refreshes statusbar
 
-	void show(SDL_Surface * to); //shows statusbar (with current text)
+	void show(SDL_Surface * to) override; //shows statusbar (with current text)
 
 	CGStatusBar(CPicture *BG, EFonts Font = FONT_SMALL, EAlignment Align = CENTER, const SDL_Color &Color = Colors::WHITE); //given CPicture will be captured by created sbar and it's pos will be used as pos for sbar
 	CGStatusBar(int x, int y, std::string name, int maxw=-1);

+ 16 - 16
client/windows/CAdvmapInterface.h

@@ -65,13 +65,13 @@ public:
 	CTerrainRect();
 	virtual ~CTerrainRect();
 	CGPath * currentPath;
-	void deactivate();
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void hover(bool on);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void deactivate() override;
+	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 show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 	void showAnim(SDL_Surface * to);
 	void showPath(const SDL_Rect * extRect, SDL_Surface * to);
 	int3 whichTileIsIt(const int & x, const int & y); //x,y are cursor position
@@ -92,14 +92,14 @@ public:
 	std::vector<std::pair<int,int> > txtpos;
 	std::string datetext;
 
-	void clickRight(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState) override;
 	CResDataBar();
 	CResDataBar(const std::string &defname, int x, int y, int offx, int offy, int resdist, int datedist);
 	~CResDataBar();
 
 	void draw(SDL_Surface * to);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 };
 
 /// That's a huge class which handles general adventure map actions and
@@ -197,11 +197,11 @@ public:
 	void fnextHero();
 	void fendTurn();
 
-	void activate();
-	void deactivate();
+	void activate() override;
+	void deactivate() override;
 
-	void show(SDL_Surface * to); //redraws terrain
-	void showAll(SDL_Surface * to); //shows and activates adv. map interface
+	void show(SDL_Surface * to) override; //redraws terrain
+	void showAll(SDL_Surface * to) override; //shows and activates adv. map interface
 
 	void select(const CArmedInstance *sel, bool centerView = true);
 	void selectionChanged();
@@ -209,8 +209,8 @@ public:
 	void centerOn(const CGObjectInstance *obj, bool fade = false);
 	int3 verifyPos(int3 ver);
 	void handleRightClick(std::string text, tribool down);
-	void keyPressed(const SDL_KeyboardEvent & key);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
 	bool isActive();
 
 	bool isHeroSleeping(const CGHeroInstance *hero);

+ 1 - 1
client/windows/CCastleInterface.cpp

@@ -768,7 +768,7 @@ void CCastleBuildings::enterCastleGate()
 		return;//only visiting hero can use castle gates
 	}
 	std::vector <int> availableTowns;
-	std::vector <const CGTownInstance*> Towns = LOCPLINT->cb->getTownsInfo(false);
+	std::vector <const CGTownInstance*> Towns = LOCPLINT->cb->getTownsInfo(true);
 	for(auto & Town : Towns)
 	{
 		const CGTownInstance *t = Town;

+ 28 - 28
client/windows/CCastleInterface.h

@@ -51,12 +51,12 @@ public:
 	CBuildingRect(CCastleBuildings * Par, const CGTownInstance *Town, const CStructure *Str); //c-tor
 	~CBuildingRect(); //d-tor
 	bool operator<(const CBuildingRect & p2) const;
-	void hover(bool on);
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void hover(bool on) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 };
 
 /// Dwelling info box - right-click screen for dwellings
@@ -88,10 +88,10 @@ public:
 	void setHighlight(bool on);
 	void set(const CGHeroInstance *newHero);
 
-	void hover (bool on);
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void deactivate();
+	void hover (bool on) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
+	void deactivate() override;
 	CHeroGSlot(int x, int y, int updown, const CGHeroInstance *h, HeroSlots * Owner); //c-tor
 	~CHeroGSlot(); //d-tor
 };
@@ -150,8 +150,8 @@ public:
 	void addBuilding(BuildingID building);
 	void removeBuilding(BuildingID building);//FIXME: not tested!!!
 	
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 };
 
 /// Creature info window
@@ -172,9 +172,9 @@ public:
 	CCreaInfo(Point position, const CGTownInstance *Town, int Level, bool compact=false, bool showAvailable=false);
 	
 	void update();
-	void hover(bool on);
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
+	void hover(bool on) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void clickRight(tribool down, bool previousState) override;
 };
 
 /// Town hall and fort icons for town screen
@@ -187,8 +187,8 @@ public:
 	//if (townHall) hall-capital else fort - castle
 	CTownInfo(int posX, int posY, const CGTownInstance* town, bool townHall);
 	
-	void hover(bool on);
-	void clickRight(tribool down, bool previousState);
+	void hover(bool on) override;
+	void clickRight(tribool down, bool previousState) override;
 };
 
 /// Class which manages the castle window
@@ -226,7 +226,7 @@ public:
 
 	void castleTeleport(int where);
 	void townChange();
-	void keyPressed(const SDL_KeyboardEvent & key);
+	void keyPressed(const SDL_KeyboardEvent & key) override;
 	void close();
 	void addBuilding(BuildingID bid);
 	void removeBuilding(BuildingID bid);
@@ -251,9 +251,9 @@ class CHallInterface : public CWindowObject
 
 	public:
 		CBuildingBox(int x, int y, const CGTownInstance * Town, const CBuilding * Building);
-		void hover(bool on);
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
+		void hover(bool on) override;
+		void clickLeft(tribool down, bool previousState) override;
+		void clickRight(tribool down, bool previousState) override;
 	};
 	const CGTownInstance * town;
 	
@@ -294,7 +294,7 @@ class LabeledValue : public CIntObject
 public:
 	LabeledValue(Rect size, std::string name, std::string descr, int min, int max);
 	LabeledValue(Rect size, std::string name, std::string descr, int val);
-	void hover(bool on);
+	void hover(bool on) override;
 };
 
 /// The fort screen where you can afford units
@@ -321,9 +321,9 @@ class CFortScreen : public CWindowObject
 		RecruitArea(int posX, int posY, const CGTownInstance *town, int level);
 		
 		void creaturesChanged();
-		void hover(bool on);
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
+		void hover(bool on) override;
+		void clickLeft(tribool down, bool previousState) override;
+		void clickRight(tribool down, bool previousState) override;
 	};
 	CLabel *title;
 	std::vector<RecruitArea*> recAreas;
@@ -349,9 +349,9 @@ class CMageGuildScreen : public CWindowObject
 
 	public:
 		Scroll(Point position, const CSpell *Spell);
-		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;
 	};
 	CPicture *window;
 	CButton *exit;

+ 2 - 2
client/windows/CHeroWindow.h

@@ -35,7 +35,7 @@ class CHeroSwitcher : public CIntObject
 	const CGHeroInstance * hero;
 	CAnimImage *image;
 public:
-	virtual void clickLeft(tribool down, bool previousState);
+	virtual void clickLeft(tribool down, bool previousState) override;
 
 	CHeroSwitcher(Point pos, const CGHeroInstance * hero);
 };
@@ -85,7 +85,7 @@ public:
 	CHeroWindow(const CGHeroInstance *hero); //c-tor
 
 	void update(const CGHeroInstance * hero, bool redrawNeeded = false); //sets main displayed hero
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 
 	void dismissCurrent(); //dissmissed currently displayed hero (curHero)
 	void questlog(); //show quest log in hero window

+ 34 - 34
client/windows/CKingdomInterface.h

@@ -74,8 +74,8 @@ public:
 	InfoBox(Point position, InfoPos Pos, InfoSize Size, IInfoBoxData *Data);
 	~InfoBox();
 
-	void clickRight(tribool down, bool previousState);
-	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState) override;
+	void clickLeft(tribool down, bool previousState) override;
 
 	//Update object if data may have changed
 	//void update();
@@ -121,13 +121,13 @@ protected:
 public:
 	InfoBoxAbstractHeroData(InfoType Type);
 
-	std::string getValueText();
-	std::string getNameText();
-	std::string getImageName(InfoBox::InfoSize size);
-	std::string getHoverText();
-	size_t getImageIndex();
+	std::string getValueText() override;
+	std::string getNameText() override;
+	std::string getImageName(InfoBox::InfoSize size) override;
+	std::string getHoverText() override;
+	size_t getImageIndex() override;
 
-	bool prepareMessage(std::string &text, CComponent **comp);
+	bool prepareMessage(std::string &text, CComponent **comp) override;
 };
 
 class InfoBoxHeroData : public InfoBoxAbstractHeroData
@@ -135,17 +135,17 @@ class InfoBoxHeroData : public InfoBoxAbstractHeroData
 	const CGHeroInstance * hero;
 	int index;//index of data in hero (0-7 for sec. skill, 0-3 for pr. skill)
 
-	int  getSubID();
-	si64 getValue();
+	int  getSubID() override;
+	si64 getValue() override;
 
 public:
 	InfoBoxHeroData(InfoType Type, const CGHeroInstance *Hero, int Index=0);
 
 	//To get a bit different texts for hero window
-	std::string getHoverText();
-	std::string getValueText();
+	std::string getHoverText() override;
+	std::string getValueText() override;
 
-	bool prepareMessage(std::string &text, CComponent **comp);
+	bool prepareMessage(std::string &text, CComponent **comp) override;
 };
 
 class InfoBoxCustomHeroData : public InfoBoxAbstractHeroData
@@ -153,8 +153,8 @@ class InfoBoxCustomHeroData : public InfoBoxAbstractHeroData
 	int subID;//subID of data (0=attack...)
 	si64 value;//actual value of data, 64-bit to fit experience and negative values
 
-	int  getSubID();
-	si64 getValue();
+	int  getSubID() override;
+	si64 getValue() override;
 
 public:
 	InfoBoxCustomHeroData(InfoType Type, int subID, si64 value);
@@ -171,13 +171,13 @@ public:
 
 	InfoBoxCustom(std::string ValueText, std::string NameText, std::string ImageName, size_t ImageIndex, std::string HoverText="");
 
-	std::string getValueText();
-	std::string getNameText();
-	std::string getImageName(InfoBox::InfoSize size);
-	std::string getHoverText();
-	size_t getImageIndex();
+	std::string getValueText() override;
+	std::string getNameText() override;
+	std::string getImageName(InfoBox::InfoSize size) override;
+	std::string getHoverText() override;
+	size_t getImageIndex() override;
 
-	bool prepareMessage(std::string &text, CComponent **comp);
+	bool prepareMessage(std::string &text, CComponent **comp) override;
 };
 
 //TODO!!!
@@ -191,11 +191,11 @@ public:
 	InfoBoxTownData(InfoType Type, const CGTownInstance * Town, int Index);
 	InfoBoxTownData(InfoType Type, int SubID, int Value);
 
-	std::string getValueText();
-	std::string getNameText();
-	std::string getImageName(InfoBox::InfoSize size);
-	std::string getHoverText();
-	size_t getImageIndex();
+	std::string getValueText() override;
+	std::string getNameText() override;
+	std::string getImageName(InfoBox::InfoSize size) override;
+	std::string getHoverText() override;
+	size_t getImageIndex() override;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -250,11 +250,11 @@ public:
 	CKingdomInterface();
 
 	void townChanged(const CGTownInstance *town);
-	void updateGarrisons();
-	void artifactRemoved(const ArtifactLocation &artLoc);
-	void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc);
-	void artifactDisassembled(const ArtifactLocation &artLoc);
-	void artifactAssembled(const ArtifactLocation &artLoc);
+	void updateGarrisons() override;
+	void artifactRemoved(const ArtifactLocation &artLoc) override;
+	void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc) override;
+	void artifactDisassembled(const ArtifactLocation &artLoc) override;
+	void artifactAssembled(const ArtifactLocation &artLoc) override;
 };
 
 /// List item with town
@@ -277,7 +277,7 @@ public:
 
 	CTownItem(const CGTownInstance* town);
 
-	void updateGarrisons();
+	void updateGarrisons() override;
 	void update();
 };
 
@@ -325,7 +325,7 @@ private:
 public:
 	CKingdHeroList(size_t maxSize);
 
-	void updateGarrisons();
+	void updateGarrisons() override;
 };
 
 /// Tab with all town-specific data
@@ -344,5 +344,5 @@ public:
 	CKingdTownList(size_t maxSize);
 	
 	void townChanged(const CGTownInstance *town);
-	void updateGarrisons();
+	void updateGarrisons() override;
 };

+ 8 - 8
client/windows/CQuestLog.h

@@ -41,8 +41,8 @@ public:
 
 	CQuestLabel (Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "")
 		: CMultiLineLabel (position, FONT_SMALL, TOPLEFT, Colors::WHITE, Text){};
-	void clickLeft(tribool down, bool previousState);
-	void showAll(SDL_Surface * to);
+	void clickLeft(tribool down, bool previousState) override;
+	void showAll(SDL_Surface * to) override;
 };
 
 class CQuestIcon : public CAnimImage
@@ -52,17 +52,17 @@ public:
 
 	CQuestIcon (const std::string &defname, int index, int x=0, int y=0);
 
-	void clickLeft(tribool down, bool previousState);
-	void showAll(SDL_Surface * to);
+	void clickLeft(tribool down, bool previousState) override;
+	void showAll(SDL_Surface * to) override;
 };
 
 class CQuestMinimap : public CMinimap
 {
 	std::vector <shared_ptr<CQuestIcon>> icons;
 
-	void clickLeft(tribool down, bool previousState){}; //minimap ignores clicking on its surface
+	void clickLeft(tribool down, bool previousState) override{}; //minimap ignores clicking on its surface
 	void iconClicked();
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent){};
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent) override{};
 
 public:
 
@@ -73,7 +73,7 @@ public:
 	void update();
 	void addQuestMarks (const QuestInfo * q);
 
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 };
 
 class CQuestLog : public CWindowObject
@@ -106,5 +106,5 @@ public:
 	void recreateLabelList();
 	void recreateQuestList (int pos);
 	void toggleComplete(bool on);
-	void showAll (SDL_Surface * to);
+	void showAll (SDL_Surface * to) override;
 };

+ 18 - 18
client/windows/CTradeWindow.h

@@ -50,10 +50,10 @@ public:
 
 		void showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to);
 
-		void clickRight(tribool down, bool previousState);
-		void hover (bool on);
-		void showAll(SDL_Surface * to);
-		void clickLeft(tribool down, bool previousState);
+		void clickRight(tribool down, bool previousState) override;
+		void hover (bool on) override;
+		void showAll(SDL_Surface * to) override;
+		void clickLeft(tribool down, bool previousState) override;
 		std::string getName(int number = -1) const;
 		CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial);
 	};
@@ -74,7 +74,7 @@ public:
 
 	CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c
 
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 
 	void initSubs(bool Left);
 	void initTypes();
@@ -109,19 +109,19 @@ public:
 	void setMax();
 	void sliderMoved(int to);
 	void makeDeal();
-	void selectionChanged(bool side); //true == left
+	void selectionChanged(bool side) override; //true == left
 	CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero = nullptr, EMarketMode::EMarketMode Mode = EMarketMode::RESOURCE_RESOURCE); //c-tor
 	~CMarketplaceWindow(); //d-tor
 
-	Point selectionOffset(bool Left) const;
-	std::string selectionSubtitle(bool Left) const;
+	Point selectionOffset(bool Left) const override;
+	std::string selectionSubtitle(bool Left) const override;
 
 
-	void garrisonChanged(); //removes creatures with count 0 from the list (apparently whole stack has been sold)
-	void artifactsChanged(bool left);
+	void garrisonChanged() override; //removes creatures with count 0 from the list (apparently whole stack has been sold)
+	void artifactsChanged(bool left) override;
 	void resourceChanged(int type, int val);
 
-	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const;
+	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const override;
 	void updateTraderText();
 };
 
@@ -141,24 +141,24 @@ public:
 	CLabel *expToLevel, *expOnAltar;
 
 
-	void selectionChanged(bool side); //true == left
+	void selectionChanged(bool side) override; //true == left
 	void SacrificeAll();
 	void SacrificeBackpack();
 
 	void putOnAltar(int backpackIndex);
 	bool putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art);
 	void makeDeal();
-	void showAll(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
 
 	void blockTrade();
 	void sliderMoved(int to);
-	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const;
+	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const override;
 	void mimicCres();
 
-	Point selectionOffset(bool Left) const;
-	std::string selectionSubtitle(bool Left) const;
-	void garrisonChanged();
-	void artifactsChanged(bool left);
+	Point selectionOffset(bool Left) const override;
+	std::string selectionSubtitle(bool Left) const override;
+	void garrisonChanged() override;
+	void artifactsChanged(bool left) override;
 	void calcTotalExp();
 	void setExpToLevel();
 	void updateRight(CTradeableItem *toUpdate);

+ 2 - 2
client/windows/CWindowObject.h

@@ -39,9 +39,9 @@ protected:
 	//Simple function with call to GH.popInt
 	void close();
 	//Used only if RCLICK_POPUP was set
-	void clickRight(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState) override;
 	//To display border
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 	//change or set background image
 	void setBackground(std::string filename);
 	void updateShadow();

+ 20 - 20
client/windows/GUIClasses.h

@@ -43,9 +43,9 @@ class CRecruitmentWindow : public CWindowObject
 		CCreaturePic *pic; //creature's animation
 		bool selected;
 
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
-		void showAll(SDL_Surface *to);
+		void clickLeft(tribool down, bool previousState) override;
+		void clickRight(tribool down, bool previousState) override;
+		void showAll(SDL_Surface *to) override;
 	public:
 		const CCreature * creature;
 		si32 amount;
@@ -88,7 +88,7 @@ class CRecruitmentWindow : public CWindowObject
 	void buy();
 	void sliderMoved(int to);
 
-	void showAll(SDL_Surface *to);
+	void showAll(SDL_Surface *to) override;
 public:
 	const CGDwelling * const dwelling;
 	CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const std::function<void(CreatureID,int)> & Recruit, int y_offset = 0); //creatures - pairs<creature_ID,amount> //c-tor
@@ -153,7 +153,7 @@ class CObjectListWindow : public CWindowObject
 		CItem(CObjectListWindow *parent, size_t id, std::string text);
 
 		void select(bool on);
-		void clickLeft(tribool down, bool previousState);
+		void clickLeft(tribool down, bool previousState) override;
 	};
 
 	std::function<void(int)> onSelect;//called when OK button is pressed, returns id of selected item.
@@ -180,7 +180,7 @@ public:
 	CIntObject *genItem(size_t index);
 	void elementSelected();//call callback and close this window
 	void changeSelection(size_t which);
-	void keyPressed (const SDL_KeyboardEvent & key);
+	void keyPressed (const SDL_KeyboardEvent & key) override;
 };
 
 class CSystemOptionsWindow : public CWindowObject
@@ -232,9 +232,9 @@ public:
 		std::string description; // "XXX is a level Y ZZZ with N artifacts"
 		const CGHeroInstance *h;
 
-		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;
 		HeroPortrait(int &sel, int id, int x, int y, const CGHeroInstance *H);
 
 	private:
@@ -255,7 +255,7 @@ public:
 
 	void recruitb();
 	void thievesguildb();
-	void show(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
 };
 
 class CExchangeWindow : public CWindowObject, public CWindowWithGarrison, public CWindowWithArtifacts
@@ -319,8 +319,8 @@ private:
 	ui8 currentAlpha;
 
 public:
-	void showAll(SDL_Surface * to);
-	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to) override;
+	void show(SDL_Surface * to) override;
 
 	CPuzzleWindow(const int3 &grailPos, double discoveredRatio);
 };
@@ -339,7 +339,7 @@ public:
 		CAnimImage *icon;
 
 		void move();
-		void clickLeft(tribool down, bool previousState);
+		void clickLeft(tribool down, bool previousState) override;
 		void update();
 		CItem(CTransformerWindow * parent, int size, int id);
 	};
@@ -353,7 +353,7 @@ public:
 	CGStatusBar *bar;
 	void makeDeal();
 	void addAll();
-	void updateGarrisons();
+	void updateGarrisons() override;
 	CTransformerWindow(const CGHeroInstance * _hero, const CGTownInstance * _town); //c-tor
 };
 
@@ -365,10 +365,10 @@ class CUniversityWindow : public CWindowObject
 		int ID;//id of selected skill
 		CUniversityWindow * parent;
 
-		void showAll(SDL_Surface * to);
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
-		void hover(bool on);
+		void showAll(SDL_Surface * to) override;
+		void clickLeft(tribool down, bool previousState) override;
+		void clickRight(tribool down, bool previousState) override;
+		void hover(bool on) override;
 		int state();//0=can't learn, 1=learned, 2=can learn
 		CItem(CUniversityWindow * _parent, int _ID, int X, int Y);
 	};
@@ -419,12 +419,12 @@ public:
 
 	CHillFortWindow(const CGHeroInstance *visitor, const CGObjectInstance *object); //c-tor
 
-	void showAll (SDL_Surface *to);
+	void showAll (SDL_Surface *to) override;
 	std::string getDefForSlot(SlotID slot);//return def name for this slot
 	std::string getTextForSlot(SlotID slot);//return hover text for this slot
 	void makeDeal(SlotID slot);//-1 for upgrading all creatures
 	int getState(SlotID slot); //-1 = no creature 0=can't upgrade, 1=upgraded, 2=can upgrade
-	void updateGarrisons();//update buttons after garrison changes
+	void updateGarrisons() override;//update buttons after garrison changes
 };
 
 class CThievesGuildWindow : public CWindowObject

+ 8 - 8
client/windows/InfoWindows.h

@@ -31,7 +31,7 @@ class CSimpleWindow : public CIntObject
 {
 public:
 	SDL_Surface * bitmap; //background
-	virtual void show(SDL_Surface * to);
+	virtual void show(SDL_Surface * to) override;
 	CSimpleWindow():bitmap(nullptr){}; //c-tor
 	virtual ~CSimpleWindow(); //d-tor
 };
@@ -53,8 +53,8 @@ public:
 	void setDelComps(bool DelComps);
 	virtual void close();
 
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 	void sliderMoved(int to);
 
 	CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo &comps = TCompsInfo(), const TButtonsInfo &Buttons = TButtonsInfo(), bool delComps = true); //c-tor
@@ -76,7 +76,7 @@ class CRClickPopup : public CIntObject
 {
 public:
 	virtual void close();
-	void clickRight(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState) override;
 
 	CRClickPopup();
 	virtual ~CRClickPopup(); //d-tor
@@ -94,8 +94,8 @@ public:
 	IShowActivatable *inner;
 	bool delInner;
 
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
 	CRClickPopupInt(IShowActivatable *our, bool deleteInt); //c-tor
 	virtual ~CRClickPopupInt(); //d-tor
 };
@@ -105,8 +105,8 @@ class CInfoPopup : public CRClickPopup
 public:
 	bool free; //TODO: comment me
 	SDL_Surface * bitmap; //popup background
-	void close();
-	void show(SDL_Surface * to);
+	void close() override;
+	void show(SDL_Surface * to) override;
 	CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free=false); //c-tor
 	CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free=false); //c-tor
 	CInfoPopup(SDL_Surface * Bitmap = nullptr, bool Free = false); //default c-tor

+ 12 - 3
config/artifacts.json

@@ -1313,7 +1313,8 @@
 				"subtype" : 35,
 				"type" : "SPELL_IMMUNITY",
 				"val" : 0,
-				"valueType" : "BASE_NUMBER"
+				"valueType" : "BASE_NUMBER",
+				"addInfo" : 1
 			}
 		],
 		"index" : 92,
@@ -1324,10 +1325,17 @@
 		"bonuses" : [
 			{
 				"type" : "NEGATE_ALL_NATURAL_IMMUNITIES",
+				"subtype" : 0,
 				"val" : 0,
 				"valueType" : "BASE_NUMBER",
 				"propagator": "BATTLE_WIDE"
-			}
+			},
+			{
+				"type" : "NEGATE_ALL_NATURAL_IMMUNITIES",
+				"subtype" : 1,
+				"val" : 0,
+				"valueType" : "BASE_NUMBER"
+			}			
 		],
 		"index" : 93,
 		"type" : ["HERO"]
@@ -1812,7 +1820,8 @@
 				"subtype" : "spell.armageddon",
 				"type" : "SPELL_IMMUNITY",
 				"val" : 0,
-				"valueType" : "BASE_NUMBER"
+				"valueType" : "BASE_NUMBER",
+				"addInfo" : 1
 			},
 			{
 				"subtype" : "primSkill.attack",

+ 5 - 0
config/bonuses.json

@@ -368,6 +368,11 @@
 			"icon":  "zvs/Lib1.res/E_OBST"
 		}
 	},
+	
+	"NO_TERRAIN_PENALTY":
+	{
+		"hidden": true
+	},
 
 	"NON_LIVING":
 	{

+ 75 - 69
config/bonuses_texts.json

@@ -1,6 +1,6 @@
 // macros:
 // ${val} - value of bonuses; Selector: type,subtype
-// ${subtype.creature} - creature name 
+// ${subtype.creature} - creature name
 // ${subtype.spell} - spell name
 // ${MR} - magic resistance of bearer
 
@@ -11,19 +11,19 @@
 		"name": "Double Strike",
 		"description": "Attacks twice"
 	},
-	
+
 	"ADDITIONAL_RETALIATION":
 	{
 		"name": "Additional retaliations",
 		"description": "May Retaliate ${val} extra times"
 	},
-	
+
 	"AIR_IMMUNITY":
 	{
-		"name": "Immune to Air",
-		"description": ""
+		"name": "Air immunity",
+		"description": "Immune to all Air school spells"
 	},
-			
+
 	"ATTACKS_ALL_ADJACENT":
 	{
 		"name": "Attack all around",
@@ -35,19 +35,19 @@
 		"name": "No retaliation",
 		"description": "Enemy cannot Retaliate"
 	},
-	
+
 	"CATAPULT":
 	{
 		"name": "Catapult",
 		"description": "Attacks siege walls"
-	},	
-	
+	},
+
 	"CHANGES_SPELL_COST_FOR_ALLY":
 	{
 		"name": "Reduce Casting Cost (${val})",
 		"description": "Reduce Casting Cost for hero"
 	},
-	
+
 	"CHANGES_SPELL_COST_FOR_ENEMY":
 	{
 		"name": "Magic Damper (${val})",
@@ -59,31 +59,31 @@
 		"name": "Immune to Charge",
 		"description": "Immune to Champion charge"
 	},
-	
+
 	"DAEMON_SUMMONING":
 	{
 		"name": "Summoner (${subtype.creature})",
-		"description": "Can rise creatures from corpses" 
+		"description": "Can rise creatures from corpses"
 	},
-	
-	"DARKNESS":		
+
+	"DARKNESS":
 	{
 		"name": "Darness cover",
 		"description": "Adds ${val} darkness radius"
 	},
-			
+
 	"DEATH_STARE":
 	{
 		"name": "Death Stare (${val}%)",
 		"description": "${val}% chance to kill single creature"
-	},	
-		
+	},
+
 	"DEFENSIVE_STANCE":
 	{
 		"name": "Defense Bonus",
 		"description": "+${val} Defense when defending"
 	},
-		
+
 	"DOUBLE_DAMAGE_CHANCE":
 	{
 		"name": "Death Blow",
@@ -95,7 +95,7 @@
 		"name": "Dragon",
 		"description": "Creature has a Dragon Nature"
 	},
-	
+
 	"DIRECT_DAMAGE_IMMUNITY":
 	{
 		"name": "Direct Damage Immunity",
@@ -104,62 +104,63 @@
 
 	"EARTH_IMMUNITY":
 	{
-		"name": "Immune to Earth",
-		"description": ""
+		"name": "Earth immunity",
+		"description": "Immune to all Earth school spells"
 	},
-					
+
 	"ENCHANTER":
 	{
 		"name": "Enchanter",
 		"description": "Can cast mass ${subtype.spell} every turn"
 	},
-	
+
 	"ENCHANTED":
 	{
 		"name": "Enchanted",
 		"description": "Affected by permanent ${subtype.spell}"
-	},		
+	},
 
 	"ENEMY_DEFENCE_REDUCTION":
 	{
 		"name": "Reduce Enemy Defense (${val}%)",
 		"description": "Reduces Defense for one attack"
 	},
-	
+
 	"GENERAL_DAMAGE_REDUCTION":
 	{
 		"name": "Reduce Damage (${val}%)",
 		"description": "Reduces physical damage"
 	},
-	
+
 	"FIRE_IMMUNITY":
 	{
-		"name": "Immune to Fire",
-		"description": ""
+		"name": "Fire immunity",
+		"description": "Immune to all Fire school spells"
 	},
 
 	"FIRE_SHIELD":
 	{
 		"name": "Fire Shield (${val}%)",
-		"description": "Reflects melee damage"		
+		"description": "Reflects melee damage"
 	},
+
 	"FEAR":
 	{
 		"name": "Fear",
 		"description": "Causes Fear on an enemy stack"
 	},
-	
+
 	"FEARLESS":
 	{
 		"name": "Fearless",
 		"description": "Immune to Fear ability"
 	},
-	
+
 	"FLYING":
 	{
 		"name": "Fly",
 		"description": "Can Fly (ignores obstacles)"
-	},		
+	},
 
 	"FREE_SHOOTING":
 	{
@@ -172,51 +173,55 @@
 		"name": "Regeneration",
 		"description": "May Regenerate full Health"
 	},
-			
+
 	"HATE":
 	{
 		"name": "Hates ${subtype.creature}",
 		"description": "Does ${val}% more damage"
 	},
-	
+
 	"HEALER":
 	{
 		"name": "Healer",
 		"description": "Heals allied units"
 	},
-	
+
 	"HP_REGENERATION":
 	{
 		"name": "Regeneration",
 		"description": "Heals ${val} hit points every round"
 	},
+
 	"JOUSTING":
 	{
 		"name": "Champion Charge",
 		"description": "+5% damage per hex travelled"
 	},
-	
+
 	"KING1":
 	{
-                "name": "King1"
+		"name": "King1",
+		"description": "Vulnerable to basic SLAYER"
 	},
-	
+
 	"KING2":
 	{
-                "name": "King2"
+		"name": "King2",
+		"description": "Vulnerable to adv. SLAYER"
 	},
-	
+
 	"KING3":
 	{
-                "name": "King3"
+		"name": "King3",
+		"description":"Vulnerable to expert SLAYER"
 	},
-	
+
 	"LEVEL_SPELL_IMMUNITY":
 	{
 		"name": "Spell immunity 1-${val}",
 		"description": "Immune to spells levels 1-${val}"
 	},
-	
+
 	"LIFE_DRAIN":
 	{
 		"name": "Drain life (${val}%)",
@@ -228,6 +233,7 @@
 		"name": "Magic Channel ${val}%",
 		"description": "Gives mana spent by enemy"
 	},
+
 	"MANA_DRAIN":
 	{
 		"name": "Mana Drain",
@@ -257,19 +263,19 @@
 		"name": "No distance penalty",
 		"description": "Full damage from any distance"
 	},
+
 	"NO_MELEE_PENALTY":
 	{
 		"name": "No melee penalty",
 		"description": "Creature has no Melee Penalty"
 	},
-	
-	
+
 	"NO_MORALE":
 	{
 
-	
-	},	
-		
+
+	},
+
 	"NO_WALL_PENALTY":
 	{
 		"name": "No wall penalty",
@@ -280,7 +286,7 @@
 	{
 		"name": "Non living",
 		"description": "Immunity to many effects"
-	},	
+	},
 
 	"RANDOM_SPELLCASTER":
 	{
@@ -298,48 +304,49 @@
 		"name": "Rebirth (${val}%)",
 		"description": "${val}% of stack will rise after death"
 	},
-								
+
 	"RETURN_AFTER_STRIKE":
 	{
 		"name": "Attack and Return",
 		"description": "Returns after melee attack"
 	},
-	
+
 	"SELF_LUCK":
 	{
 		"name": "Positive luck",
 		"description": "Always has Positive Luck"
 	},
-	
+
 	"SELF_MORALE":
 	{
 		"name": "Positive morale",
 		"description": "Always has Positive Morale"
 	},
-	
+
 	"SHOOTER":
 	{
 		"name": "Ranged",
 		"description": "Creature can shoot"
-	},	
+	},
+
 	"SPELLCASTER":
 	{
 		"name": "Spellcaster",
 		"description": "Can cast ${subtype.spell}"
 	},
-					
+
 	"SPELL_AFTER_ATTACK":
 	{
-		"name": "Caster - ${subtype.spell}",
-		"description": "${val}% chance to cast after attack"
+		"name": "After attack cast",
+		"description": "${val}% to cast ${subtype.spell} after attack"
 	},
-	
+
 	"SPELL_BEFORE_ATTACK":
 	{
-		"name": "Caster - ${subtype.spell}",
-		"description": "${val}% chance to cast before attack"
+		"name": "Before attack cast",
+		"description": "${val}% to cast ${subtype.spell} before attack"
 	},
-	
+
 	"SPELL_DAMAGE_REDUCTION":
 	{
 		"name": "Spell Resistance",
@@ -348,17 +355,16 @@
 
 	"SPELL_IMMUNITY":
 	{
-		"name": "Immune to ${subtype.spell}",
-		"description": ""
+		"name": "Spell immunity",
+		"description": "Immune to ${subtype.spell}"
 	},
 
-	
 	"SPELL_LIKE_ATTACK":
 	{
 		"name": "Spell-like attack",
 		"description": "Attacks with ${subtype.spell}"
 	},
-			
+
 	"SPELL_RESISTANCE_AURA":
 	{
 		"name": "Aura of Resistance",
@@ -371,8 +377,6 @@
 		"description": "Breath Attack (2-hex range)"
 	},
 
-
-
 	"THREE_HEADED_ATTACK":
 	{
 		"name": "Three-headed attack",
@@ -384,14 +388,16 @@
 		"name": "Undead",
 		"description": "Creature is Undead"
 	},
+
 	"UNLIMITED_RETALIATIONS":
 	{
 		"name": "Unlimited retaliations",
 		"description": "Retaliate any number of attacks"
 	},
+
 	"WATER_IMMUNITY":
 	{
-		"name": "Immune to Water",
-		"description": ""
+		"name": "Water immunity",
+		"description": "Immune to all Water school spells"
 	}
 }

+ 8 - 4
config/creatures/dungeon.json

@@ -9,12 +9,14 @@
 			"blindImmunity" : 
 			{
 				"type" : "SPELL_IMMUNITY",
-				"subtype" : "spell.blind"
+				"subtype" : "spell.blind",
+				"addInfo" : 1
 			},
 			"petrifyImmunity" : 
 			{
 				"type" : "SPELL_IMMUNITY",
-				"subtype" : "spell.stoneGaze"
+				"subtype" : "spell.stoneGaze",
+				"addInfo" : 1
 			}
 		},
 		"upgrades": ["infernalTroglodyte"],
@@ -42,12 +44,14 @@
 			"blindImmunity" : 
 			{
 				"type" : "SPELL_IMMUNITY",
-				"subtype" : "spell.blind"
+				"subtype" : "spell.blind",
+				"addInfo" : 1
 			},
 			"petrifyImmunity" : 
 			{
 				"type" : "SPELL_IMMUNITY",
-				"subtype" : "spell.stoneGaze"
+				"subtype" : "spell.stoneGaze",
+				"addInfo" : 1
 			}
 		},
 		"graphics" :

+ 9 - 0
config/creatures/neutral.json

@@ -475,6 +475,15 @@
 		"index": 142,
 		"level": 3,
 		"faction": "neutral",
+		"abilities":
+		{
+			"sandWalker" :
+			{
+				"type" : "NO_TERRAIN_PENALTY",
+				"subtype" : 1,
+				"propagator" : "HERO"
+			}
+		},
 		"doubleWide" : true,
 		"graphics" :
 		{

+ 1 - 1
config/spells/timed.json

@@ -564,7 +564,7 @@
 						"subtype" : "primSkill.defence",
 						"val" : -3,
 						"valueType" : "ADDITIVE_VALUE",
-						"duration" : "N_TURNS"
+						"duration" : "PERMANENT"
 					}
 				}
 			},

+ 41 - 1
lib/BattleState.cpp

@@ -1170,6 +1170,17 @@ bool CStack::canBeHealed() const
 		&& !hasBonusOfType(Bonus::SIEGE_WEAPON);
 }
 
+ui32 CStack::calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const
+{
+	if(!resurrect && !alive())
+	{
+		logGlobal->warnStream() <<"Attempt to heal corpse detected.";
+		return 0;
+	}
+
+	return std::min<ui32>(toHeal, MaxHealth() - firstHPleft + (resurrect ? (baseAmount - count) * MaxHealth() : 0));
+}
+
 ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) const
 {
 	int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->id));
@@ -1181,10 +1192,39 @@ ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) c
 
 ui32 CStack::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const
 {
-	//stacks does not have spellpower etc. (yet?)
+	//stacks does not have sorcery-like bonuses (yet?)
 	return base;
 }
 
+int CStack::getEffectLevel(const CSpell * spell) const
+{
+	return getSpellSchoolLevel(spell);
+}
+
+int CStack::getEffectPower(const CSpell * spell) const
+{
+	return valOfBonuses(Bonus::CREATURE_SPELL_POWER) * count / 100;
+}
+
+int CStack::getEnchantPower(const CSpell * spell) const
+{
+	int res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
+	if(res<=0)
+		res = 3;//default for creatures
+	return res;
+}
+
+int CStack::getEffectValue(const CSpell * spell) const
+{
+	return valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spell->id.toEnum()) * count;
+}
+
+const PlayerColor CStack::getOwner() const
+{
+	return owner;
+}
+
+
 bool CMP_stack::operator()( const CStack* a, const CStack* b )
 {
 	switch(phase)

+ 16 - 0
lib/BattleState.h

@@ -203,6 +203,8 @@ public:
 	bool waited(int turn = 0) const;
 	bool canMove(int turn = 0) const; //if stack can move
 	bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines
+	///returns actual heal value based on internal state
+	ui32 calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const;
 	ui32 level() const;
 	si32 magicResistance() const override; //include aura of resistance
 	static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse);
@@ -233,6 +235,20 @@ public:
 	///ISpellCaster
 	ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
 	ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
+
+	///default spell school level for effect calculation
+	int getEffectLevel(const CSpell * spell) const override;
+
+	///default spell-power for damage/heal calculation
+	int getEffectPower(const CSpell * spell) const override;
+
+	///default spell-power for timed effects duration
+	int getEnchantPower(const CSpell * spell) const override;
+
+	///damage/heal override(ignores spell configuration, effect level and effect power)
+	int getEffectValue(const CSpell * spell) const override;
+
+	const PlayerColor getOwner() const override;
 	
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 9 - 12
lib/CArtHandler.cpp

@@ -577,10 +577,12 @@ bool CArtHandler::legalArtifact(ArtifactID id)
 {
 	auto art = artifacts[id];
 	//assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components
-	return (art->possibleSlots[ArtBearer::HERO].size() ||
-			(art->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS) ||
-			(art->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT)) &&
-			!(art->constituents); //no combo artifacts spawning
+	return ((art->possibleSlots[ArtBearer::HERO].size() ||
+		(art->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS) ||
+		(art->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT)) &&
+		!(art->constituents) && //no combo artifacts spawning
+		art->aClass >= CArtifact::ART_TREASURE &&
+		art->aClass <= CArtifact::ART_RELIC);
 }
 
 bool CArtHandler::isTradableArtifact(ArtifactID id) const
@@ -609,17 +611,12 @@ void CArtHandler::initAllowedArtifactsList(const std::vector<bool> &allowed)
 
 	for (ArtifactID i=ArtifactID::SPELLBOOK; i<ArtifactID::ART_SELECTION; i.advance(1))
 	{
+		//check artifacts allowed on a map
+		//TODO: This line will be different when custom map format is implemented
 		if (allowed[i] && legalArtifact(i))
 			allowedArtifacts.push_back(artifacts[i]);
 	}
-	if (VLC->modh->modules.COMMANDERS) //allow all commander artifacts for testing
-	{
-		for (int i = 146; i <= 155; ++i)
-		{
-			allowedArtifacts.push_back(artifacts[i]);
-		}
-	}
-	for (int i = GameConstants::ARTIFACTS_QUANTITY; i < artifacts.size(); ++i) //allow all new artifacts by default
+	for (ArtifactID i = ArtifactID::ART_SELECTION; i<ArtifactID(artifacts.size()); i.advance(1)) //try to allow all artifacts added by mods
 	{
 		if (legalArtifact(ArtifactID(i)))
 			allowedArtifacts.push_back(artifacts[i]);

+ 1 - 1
lib/CArtHandler.h

@@ -93,7 +93,7 @@ public:
 	std::vector <std::pair <ui16, Bonus> > bonusesPerLevel; //bonus given each n levels
 	std::vector <std::pair <ui16, Bonus> > thresholdBonuses; //after certain level they will be added once
 
-	void levelUpArtifact (CArtifactInstance * art);
+	void levelUpArtifact (CArtifactInstance * art) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 38 - 42
lib/CBattleCallback.cpp

@@ -335,6 +335,11 @@ int CBattleInfoEssentials::battleCastSpells(ui8 side) const
 	return getBattle()->sides[side].castSpellsCount;
 }
 
+const IBonusBearer * CBattleInfoEssentials::getBattleNode() const
+{
+	return getBattle();
+}
+
 ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(PlayerColor player, ECastingMode::ECastingMode mode) const
 {
 	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
@@ -1587,9 +1592,15 @@ std::vector<BattleHex> CBattleInfoCallback::getAttackableBattleHexes() const
 	return attackableBattleHexes;
 }
 
-ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell( PlayerColor player, const CSpell * spell, ECastingMode::ECastingMode mode ) const
+ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode) const
 {
 	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
+	if(caster == nullptr)
+	{
+		logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastThisSpell: no spellcaster.";
+		return ESpellCastProblem::INVALID;
+	}
+	const PlayerColor player = caster->getOwner();
 	const ui8 side = playerToSide(player);
 	if(!battleDoWeKnowAbout(side))
 		return ESpellCastProblem::INVALID;
@@ -1598,16 +1609,11 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 	if(genProblem != ESpellCastProblem::OK)
 		return genProblem;
 
-	//Casting hero, set only if he is an actual caster.
-	const CGHeroInstance *castingHero = mode == ECastingMode::HERO_CASTING
-										? battleGetFightingHero(side)
-										: nullptr;
-
-
 	switch(mode)
 	{
 	case ECastingMode::HERO_CASTING:
 		{
+			const CGHeroInstance * castingHero = dynamic_cast<const CGHeroInstance *>(caster);//todo: unify hero|creature spell cost
 			assert(castingHero);
 			if(!castingHero->canCastThisSpell(spell))
 				return ESpellCastProblem::HERO_DOESNT_KNOW_SPELL;
@@ -1617,11 +1623,10 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 		break;
 	}
 
-
 	if(!spell->combatSpell)
 		return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
 
-	const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCasted(this, player);
+	const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, player);
 	
 	if(specificProblem != ESpellCastProblem::OK)
 		return specificProblem;	
@@ -1634,7 +1639,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 		auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
 		for(auto stack : stacks)
 		{
-			if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, stack))
+			if(ESpellCastProblem::OK == spell->isImmuneByStack(caster, stack))
 			{
 				allStacksImmune = false;
 				break;
@@ -1645,7 +1650,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 	}
 
-	if(battleMaxSpellLevel() < spell->level) //effect like Recanter's Cloak or Orb of Inhibition
+	if(battleMaxSpellLevel(side) < spell->level) //effect like Recanter's Cloak or Orb of Inhibition
 		return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
 
 	//checking if there exists an appropriate target
@@ -1654,8 +1659,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 	case CSpell::CREATURE:
 		if(mode == ECastingMode::HERO_CASTING)
 		{
-			const CGHeroInstance * caster = battleGetFightingHero(side);
-			const CSpell::TargetInfo ti = spell->getTargetInfo(caster->getSpellSchoolLevel(spell));
+			const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell));
 			bool targetExists = false;
 
             for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
@@ -1710,7 +1714,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
 	case CSpell::CREATURE:
 		{
 			const CGHeroInstance * caster = battleGetFightingHero(playerToSide(player)); //TODO
-			const CSpell::TargetInfo ti = spell->getTargetInfo(caster->getSpellSchoolLevel(spell));
+			const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell));
 			
 			for(const CStack * stack : battleAliveStacks())
 			{
@@ -1772,10 +1776,16 @@ ui32 CBattleInfoCallback::battleGetSpellCost(const CSpell * sp, const CGHeroInst
 	return ret - manaReduction + manaIncrease;
 }
 
-ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpellHere( PlayerColor player, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest ) const
+ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const
 {
 	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
-	ESpellCastProblem::ESpellCastProblem moreGeneralProblem = battleCanCastThisSpell(player, spell, mode);
+	if(caster == nullptr)
+	{
+		logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastThisSpellHere: no spellcaster.";
+		return ESpellCastProblem::INVALID;
+	}	
+	const PlayerColor player = caster->getOwner();
+	ESpellCastProblem::ESpellCastProblem moreGeneralProblem = battleCanCastThisSpell(caster, spell, mode);
 	if(moreGeneralProblem != ESpellCastProblem::OK)
 		return moreGeneralProblem;
 
@@ -1826,8 +1836,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 	{
 		if(!deadStack && !aliveStack)
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
-		if(spell->id == SpellID::ANIMATE_DEAD  &&  deadStack  &&  !deadStack->hasBonusOfType(Bonus::UNDEAD))
-			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 		if(deadStack && deadStack->owner != player) //you can resurrect only your own stacks //FIXME: it includes alive stacks as well
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 	}
@@ -1840,11 +1848,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 		if(spell->isPositive() && aliveStack->owner != player)
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 	}
-
-	const CGHeroInstance * caster = nullptr;
-	if (mode == ECastingMode::HERO_CASTING)
-		caster = battleGetFightingHero(playerToSide(player));
-	
 	return spell->isImmuneAt(this, caster, mode, dest);
 }
 
@@ -1927,7 +1930,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
 	{
 		if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, spellID)
 			//TODO: this ability has special limitations
-			|| battleCanCastThisSpellHere(subject->owner, spellID.toSpell(), ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
+			|| battleCanCastThisSpellHere(subject, spellID.toSpell(), ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
 			continue;
 
 		switch (spellID)
@@ -2060,12 +2063,14 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const
 	return ret;
 }
 
-si8 CBattleInfoCallback::battleMaxSpellLevel() const
+si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const
 {
-	const CBonusSystemNode *node = nullptr;
-	if(const CGHeroInstance *h =  battleGetFightingHero(battleGetMySide()))
+	const IBonusBearer *node = nullptr;
+	if(const CGHeroInstance * h = battleGetFightingHero(side))
 		node = h;
-	//TODO else use battle node
+	else
+		node = getBattleNode();
+
 	if(!node)
 		return GameConstants::SPELL_LEVELS;
 
@@ -2163,21 +2168,12 @@ ESpellCastProblem::ESpellCastProblem CPlayerBattleCallback::battleCanCastThisSpe
 {
 	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
 	ASSERT_IF_CALLED_WITH_PLAYER
-	return CBattleInfoCallback::battleCanCastThisSpell(*player, spell, ECastingMode::HERO_CASTING);
-}
 
-ESpellCastProblem::ESpellCastProblem CPlayerBattleCallback::battleCanCastThisSpell(const CSpell * spell, BattleHex destination) const
-{
-	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
-	ASSERT_IF_CALLED_WITH_PLAYER
-	return battleCanCastThisSpellHere(*player, spell, ECastingMode::HERO_CASTING, destination);
-}
-
-ESpellCastProblem::ESpellCastProblem CPlayerBattleCallback::battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination) const
-{
-	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
-	ASSERT_IF_CALLED_WITH_PLAYER
-	return battleCanCastThisSpellHere(*player, spell, ECastingMode::CREATURE_ACTIVE_CASTING, destination);
+	const ISpellCaster * hero = battleGetMyHero();
+	if(hero == nullptr)
+		return ESpellCastProblem::INVALID;
+	else
+		return CBattleInfoCallback::battleCanCastThisSpell(hero, spell, ECastingMode::HERO_CASTING);
 }
 
 bool CPlayerBattleCallback::battleCanFlee() const

+ 8 - 8
lib/CBattleCallback.h

@@ -15,6 +15,7 @@ class CGameState;
 class CGTownInstance;
 class CGHeroInstance;
 class CStack;
+class ISpellCaster;
 class CSpell;
 struct BattleInfo;
 struct CObstacleInstance;
@@ -158,6 +159,7 @@ class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase
 {
 protected:
 	bool battleDoWeKnowAbout(ui8 side) const;
+	const IBonusBearer * getBattleNode() const;
 public:
 	enum EStackOwnership
 	{
@@ -189,7 +191,7 @@ public:
 	ui8 playerToSide(PlayerColor player) const;
 	ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
 	bool battleHasHero(ui8 side) const;
-	int battleCastSpells(ui8 side) const; //how many spells has given side casted
+	int battleCastSpells(ui8 side) const; //how many spells has given side cast
 	const CGHeroInstance * battleGetFightingHero(ui8 side) const; //depracated for players callback, easy to get wrong
 	const CArmedInstance * battleGetArmyObject(ui8 side) const; 
 	InfoAboutHero battleGetHeroInfo(ui8 side) const;
@@ -276,12 +278,11 @@ public:
 	std::vector<BattleHex> getAttackableBattleHexes() const;
 
 	//*** MAGIC 
-	si8 battleMaxSpellLevel() const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
+	si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
 	ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
 	ESpellCastProblem::ESpellCastProblem battleCanCastSpell(PlayerColor player, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
-	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(PlayerColor player, const CSpell * spell, ECastingMode::ECastingMode mode) const; //checks if given player can cast given spell
-	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(PlayerColor player, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode
-	ESpellCastProblem::ESpellCastProblem battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination) const; //determines if creature can cast a spell here
+	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode) const; //checks if given player can cast given spell
+	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode
 	std::vector<BattleHex> battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const;
 
 	SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const;
@@ -324,9 +325,8 @@ class DLL_LINKAGE CPlayerBattleCallback : public CBattleInfoCallback
 public:
 	bool battleCanFlee() const; //returns true if caller can flee from the battle
 	TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true) const; //returns stacks on battlefield
-	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell) const; //determines if given spell can be casted (and returns problem description)
-	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell, BattleHex destination) const; //if hero can cast spell here
-	ESpellCastProblem::ESpellCastProblem battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination) const; //determines if creature can cast a spell here
+	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell) const; //determines if given spell can be cast (and returns problem description)
+
 	int battleGetSurrenderCost() const; //returns cost of surrendering battle, -1 if surrendering is not possible
 
 	bool battleCanCastSpell(ESpellCastProblem::ESpellCastProblem *outProblem = nullptr) const; //returns true, if caller can cast a spell. If not, if pointer is given via arg, the reason will be written.

+ 8 - 0
lib/CCreatureHandler.cpp

@@ -853,34 +853,42 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
 			case 'B': //Blind
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::BLIND;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case 'H': //Hypnotize
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::HYPNOTIZE;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case 'I': //Implosion
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::IMPLOSION;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case 'K': //Berserk
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::BERSERK;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case 'M': //Meteor Shower
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::METEOR_SHOWER;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case 'N': //dispell beneficial spells
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::DISPEL_HELPFUL_SPELLS;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case 'R': //Armageddon
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::ARMAGEDDON;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case 'S': //Slow
 				b.type = Bonus::SPELL_IMMUNITY;
 				b.subtype = SpellID::SLOW;
+				b.additionalInfo = 0;//normal immunity
 				break;
 			case '6':
 			case '7':

+ 1 - 1
lib/CCreatureHandler.h

@@ -199,7 +199,7 @@ public:
 	/// generates tier-specific bonus tree entries
 	void buildBonusTreeForTiers();
 
-	void afterLoadFinalization();
+	void afterLoadFinalization() override;
 
 	std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
 

+ 4 - 4
lib/CCreatureSet.h

@@ -68,7 +68,7 @@ public:
 	std::string getQuantityTXT(bool capitalized = true) const;
 	virtual int getExpRank() const;
 	virtual int getLevel() const; //different for regular stack and commander
-	si32 magicResistance() const;
+	si32 magicResistance() const override;
 	CreatureID getCreatureID() const; //-1 if not available
 	std::string getName() const; //plural or singular
 	virtual void init();
@@ -104,12 +104,12 @@ public:
 	CCommanderInstance (CreatureID id);
 	~CCommanderInstance();
 	void setAlive (bool alive);
-	void giveStackExp (TExpType exp);
+	void giveStackExp (TExpType exp) override;
 	void levelUp ();
 
 	bool gainsLevel() const; //true if commander has lower level than should upon his experience
-	ui64 getPower() const {return 0;};
-	int getExpRank() const;
+	ui64 getPower() const override {return 0;};
+	int getExpRank() const override;
 	int getLevel() const override; 
 	ArtBearer::ArtBearer bearerType() const override; //from CArtifactSet
 

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -182,7 +182,7 @@ int CGameInfoCallback::estimateSpellDamage(const CSpell * sp, const CGHeroInstan
 	ERROR_RET_VAL_IF(hero && !canGetFullInfo(hero), "Cannot get info about caster!", -1);
 
 	if (hero) //we see hero's spellbook
-		return sp->calculateDamage(hero, nullptr, hero->getSpellSchoolLevel(sp), hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER));
+		return sp->calculateDamage(hero, nullptr, hero->getEffectLevel(sp), hero->getEffectPower(sp));
 	else
 		return 0; //mage guild
 }

+ 21 - 21
lib/CGameInterface.h

@@ -127,26 +127,26 @@ public:
 	virtual std::string getBattleAIName() const = 0; //has to return name of the battle AI to be used
 
 	//battle interface
-	virtual BattleAction activeStack(const CStack * stack);
-	virtual void yourTacticPhase(int distance);
-	virtual void battleNewRound(int round);
-	virtual void battleCatapultAttacked(const CatapultAttack & ca);
-	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side);
-	virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa);
-	virtual void actionStarted(const BattleAction &action);
-	virtual void battleNewRoundFirst(int round);
-	virtual void actionFinished(const BattleAction &action);
-	virtual void battleStacksEffectsSet(const SetStackEffect & sse);
+	virtual BattleAction activeStack(const CStack * stack) override;
+	virtual void yourTacticPhase(int distance) override;
+	virtual void battleNewRound(int round) override;
+	virtual void battleCatapultAttacked(const CatapultAttack & ca) override;
+	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override;
+	virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override;
+	virtual void actionStarted(const BattleAction &action) override;
+	virtual void battleNewRoundFirst(int round) override;
+	virtual void actionFinished(const BattleAction &action) override;
+	virtual void battleStacksEffectsSet(const SetStackEffect & sse) override;
 	//virtual void battleTriggerEffect(const BattleTriggerEffect & bte);
-	virtual void battleStacksRemoved(const BattleStacksRemoved & bsr);
-	virtual void battleObstaclesRemoved(const std::set<si32> & removedObstacles);
-	virtual void battleNewStackAppeared(const CStack * stack);
-	virtual void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance);
-	virtual void battleAttack(const BattleAttack *ba);
-	virtual void battleSpellCast(const BattleSpellCast *sc);
-	virtual void battleEnd(const BattleResult *br);
-	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom);
-
-	virtual void saveGame(COSer & h, const int version); //saving
-	virtual void loadGame(CISer & h, const int version); //loading
+	virtual void battleStacksRemoved(const BattleStacksRemoved & bsr) override;
+	virtual void battleObstaclesRemoved(const std::set<si32> & removedObstacles) override;
+	virtual void battleNewStackAppeared(const CStack * stack) override;
+	virtual void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance) override;
+	virtual void battleAttack(const BattleAttack *ba) override;
+	virtual void battleSpellCast(const BattleSpellCast *sc) override;
+	virtual void battleEnd(const BattleResult *br) override;
+	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) override;
+
+	virtual void saveGame(COSer & h, const int version) override; //saving
+	virtual void loadGame(CISer & h, const int version) override; //loading
 };

+ 1 - 1
lib/CGameState.cpp

@@ -63,7 +63,7 @@ public:
 template <typename T> class CApplyOnGS : public CBaseForGSApply
 {
 public:
-	void applyOnGS(CGameState *gs, void *pack) const
+	void applyOnGS(CGameState *gs, void *pack) const override
 	{
 		T *ptr = static_cast<T*>(pack);
 

+ 1 - 1
lib/CGeneralTextHandler.cpp

@@ -159,7 +159,7 @@ void Unicode::trimRight(std::string & text, const size_t amount/* =1 */)
 class LocaleWithComma: public std::numpunct<char>
 {
 protected:
-	char do_decimal_point() const
+	char do_decimal_point() const override
 	{
 		return ',';
 	}

+ 3 - 3
lib/CHeroHandler.h

@@ -178,9 +178,9 @@ public:
 	void loadObject(std::string scope, std::string name, const JsonNode & data) override;
 	void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
 
-	void afterLoadFinalization();
+	void afterLoadFinalization() override;
 
-	std::vector<bool> getDefaultAllowed() const;
+	std::vector<bool> getDefaultAllowed() const override;
 
 	~CHeroClassHandler();
 
@@ -244,7 +244,7 @@ public:
 	CHeroHandler(); //c-tor
 	~CHeroHandler(); //d-tor
 
-	std::vector<bool> getDefaultAllowed() const;
+	std::vector<bool> getDefaultAllowed() const override;
 
 	/**
 	 * Gets a list of default allowed abilities. OH3 abilities/skills are all allowed by default.

+ 3 - 3
lib/Connection.h

@@ -1551,7 +1551,7 @@ public:
 
 	void openNextFile(const std::string &fname); //throws!
 	void clear();
-    void reportState(CLogger * out);
+    void reportState(CLogger * out) override;
 
 	void putMagicBytes(const std::string &text);
 	
@@ -1578,7 +1578,7 @@ public:
 
 	void openNextFile(const boost::filesystem::path & fname, int minimalVersion); //throws!
 	void clear();
-    void reportState(CLogger * out);
+    void reportState(CLogger * out) override;
 
 	void checkMagicBytes(const std::string & text);
 	
@@ -1616,7 +1616,7 @@ class DLL_LINKAGE CConnection
 	CConnection(void);
 
 	void init();
-    void reportState(CLogger * out);
+    void reportState(CLogger * out) override;
 public:
 	CISer iser;
 	COSer oser;

+ 6 - 2
lib/GameConstants.h

@@ -14,7 +14,7 @@
 
 namespace GameConstants
 {
-	const std::string VCMI_VERSION = "VCMI 0.98d";
+	const std::string VCMI_VERSION = "VCMI 0.98e";
 
 	const int BFIELD_WIDTH = 17;
 	const int BFIELD_HEIGHT = 11;
@@ -51,6 +51,7 @@ namespace GameConstants
 	const int SPELLS_QUANTITY=70;
 	const int CREATURES_COUNT = 197;
 
+	const ui32 BASE_MOVEMENT_COST = 100; //default cost for non-diagonal movement
 }
 
 class CArtifact;
@@ -414,7 +415,8 @@ namespace ECastingMode
 	{
 		HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack
 		MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING,
-		SPELL_LIKE_ATTACK	
+		SPELL_LIKE_ATTACK, 
+		PASSIVE_CASTING//f.e. opening battle spells
 	};
 }
 
@@ -699,6 +701,7 @@ namespace Battle
 {
 	enum ActionType
 	{
+		CANCEL = -3,
 		END_TACTIC_PHASE = -2,
 		INVALID = -1,
 		NO_ACTION = 0,
@@ -823,6 +826,7 @@ public:
 		//BLACKSHARD_OF_THE_DEAD_KNIGHT = 8,
 		TITANS_THUNDER = 135,
 		//CORNUCOPIA = 140,
+		//FIXME: the following is only true if WoG is enabled. Otherwise other mod artifacts will take these slots.
 		ART_SELECTION = 144,
 		ART_LOCK = 145,
 		AXE_OF_SMASHING = 146,

+ 1 - 1
lib/HeroBonus.cpp

@@ -504,7 +504,7 @@ const TBonusListPtr IBonusBearer::getSpellBonuses() const
 	CSelector selector = Selector::sourceType(Bonus::SPELL_EFFECT)
 		.And(CSelector([](const Bonus * b)->bool
 		{
-			return !b->type == Bonus::NONE;
+			return b->type != Bonus::NONE;
 		})); 
 	return getBonuses(selector, Selector::anyRange(), cachingStr.str());
 }

+ 8 - 6
lib/HeroBonus.h

@@ -133,7 +133,7 @@ public:
 	BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
 	BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus*/ \
 	BONUS_NAME(BLOCK_MAGIC_ABOVE) /*blocks casting spells of the level > value */ \
-	BONUS_NAME(BLOCK_ALL_MAGIC) /*blocks casting spells of the level > value */ \
+	BONUS_NAME(BLOCK_ALL_MAGIC) /*blocks casting spells*/ \
 	BONUS_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/	\
 	BONUS_NAME(SPELL_DAMAGE_REDUCTION) /*eg. golems; value - reduction in %, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/ \
 	BONUS_NAME(NO_WALL_PENALTY)							\
@@ -207,16 +207,18 @@ public:
 	BONUS_NAME(RECEPTIVE) /*accepts friendly spells even with immunity*/\
 	BONUS_NAME(DIRECT_DAMAGE_IMMUNITY) /*direct damage spells, that is*/\
 	BONUS_NAME(CASTS) /*how many times creature can cast activated spell*/ \
-	BONUS_NAME(SPECIFIC_SPELL_POWER) /* value used for Thunderbolt and Resurrection casted by units, subtype - spell id */\
+	BONUS_NAME(SPECIFIC_SPELL_POWER) /* value used for Thunderbolt and Resurrection cast by units, subtype - spell id */\
 	BONUS_NAME(CREATURE_SPELL_POWER) /* value per unit, divided by 100 (so faerie Dragons have 800)*/ \
-	BONUS_NAME(CREATURE_ENCHANT_POWER) /* total duration of spells casted by creature */ \
+	BONUS_NAME(CREATURE_ENCHANT_POWER) /* total duration of spells cast by creature */ \
 	BONUS_NAME(ENCHANTED) /* permanently enchanted with spell subID of level = val, if val > 3 then spell is mass and has level of val-3*/ \
 	BONUS_NAME(REBIRTH) /* val - percent of life restored, subtype = 0 - regular, 1 - at least one unit (sacred Phoenix) */\
 	BONUS_NAME(ADDITIONAL_UNITS) /*val of units with id = subtype will be added to hero's army at the beginning of battle */\
 	BONUS_NAME(SPOILS_OF_WAR) /*val * 10^-6 * gained exp resources of subtype will be given to hero after battle*/\
 	BONUS_NAME(BLOCK)\
 	BONUS_NAME(DISGUISED) /* subtype - spell level */\
-	BONUS_NAME(VISIONS) /* subtype - spell level */
+	BONUS_NAME(VISIONS) /* subtype - spell level */\
+	BONUS_NAME(NO_TERRAIN_PENALTY) /* subtype - terrain type */\
+	/* end of list */
 	
 
 #define BONUS_SOURCE_LIST \
@@ -539,7 +541,7 @@ class DLL_LINKAGE CPropagatorNodeType : public IPropagator
 public:
 	CPropagatorNodeType();
 	CPropagatorNodeType(int NodeType);
-	bool shouldBeAttached(CBonusSystemNode *dest);
+	bool shouldBeAttached(CBonusSystemNode *dest) override;
 	//CBonusSystemNode *getDestNode(CBonusSystemNode *source, CBonusSystemNode *redParent, CBonusSystemNode *redChild) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -651,7 +653,7 @@ public:
 
 	void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
 	TBonusListPtr limitBonuses(const BonusList &allBonuses) const; //same as above, returns out by val for convienence
-	const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const;
+	const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
 	void getParents(TCNodes &out) const;  //retrieves list of parent nodes (nodes to inherit bonuses from),
 	const Bonus *getBonusLocalFirst(const CSelector &selector) const;
 

+ 6 - 6
lib/NetPacks.h

@@ -167,10 +167,11 @@ struct YourTurn : public CPackForClient //100
 	DLL_LINKAGE void applyGs(CGameState *gs);
 
 	PlayerColor player;
+	boost::optional<ui8> daysWithoutCastle;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & player;
+		h & player & daysWithoutCastle;
 	}
 };
 
@@ -986,7 +987,6 @@ struct NewTurn : public CPackForClient //101
 	std::map<PlayerColor, TResources> res; //player ID => resource value[res_id]
 	std::map<ObjectInstanceID, SetAvailableCreatures> cres;//creatures to be placed in towns
 	ui32 day;
-	bool resetBuilded;
 	ui8 specialWeek; //weekType
 	CreatureID creatureid; //for creature weeks
 
@@ -994,7 +994,7 @@ struct NewTurn : public CPackForClient //101
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & heroes & cres & res & day & resetBuilded & specialWeek & creatureid;
+		h & heroes & cres & res & day & specialWeek & creatureid;
 	}
 };
 
@@ -1324,7 +1324,7 @@ struct StacksHealedOrResurrected : public CPackForClient //3013
 	{
 		ui32 stackID;
 		ui32 healedHP;
-		ui8 lowLevelResurrection; //in case this stack is resurrected by this heal, it will be marked as SUMMONED //TODO: replace with separate counter
+		bool lowLevelResurrection; //in case this stack is resurrected by this heal, it will be marked as SUMMONED //TODO: replace with separate counter
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
 			h & stackID & healedHP & lowLevelResurrection;
@@ -1503,7 +1503,7 @@ struct BattleSpellCast : public CPackForClient//3009
 	std::vector<CustomEffect> customEffects;
 	std::set<ui32> affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure)
 	si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID
-	bool castByHero; //if true - spell has been casted by hero, otherwise by a creature
+	bool castByHero; //if true - spell has been cast by hero, otherwise by a creature
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & dmgToDisplay & side & id & skill & manaGained & tile & customEffects & affectedCres & casterStack & castByHero;
@@ -1605,7 +1605,7 @@ struct BattleStacksRemoved : public CPackForClient //3016
 	BattleStacksRemoved(){type = 3016;}
 
 	DLL_LINKAGE void applyGs(CGameState *gs);
-	void applyCl(CClient *cl);
+	void applyFirstCl(CClient *cl);//inform client before stack objects are destroyed
 
 	std::set<ui32> stackIDs; //IDs of removed stacks
 

+ 1 - 1
lib/NetPacksBase.h

@@ -115,7 +115,7 @@ public:
 		numbers.clear();
 	}
 	void toString(std::string &dst) const;
-	std::string toString() const;
+	std::string toString() const override;
 	void getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const;
 
 	MetaString()

+ 15 - 23
lib/NetPacksLib.cpp

@@ -468,6 +468,12 @@ static int getDir(int3 src, int3 dst)
 void TryMoveHero::applyGs( CGameState *gs )
 {
 	CGHeroInstance *h = gs->getHero(id);
+	if (!h)
+	{
+		logGlobal->errorStream() << "Attempt ot move unavailable hero " << id;
+		return;
+	}
+
 	h->movement = movePoints;
 
 	if((result == SUCCESS || result == BLOCKING_VISIT || result == EMBARK || result == DISEMBARK) && start != end)
@@ -1136,7 +1142,7 @@ DLL_LINKAGE void BattleTriggerEffect::applyGs( CGameState *gs )
 		}
 		case Bonus::POISON:
 		{
-			Bonus * b = st->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT, 71)
+			Bonus * b = st->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT, SpellID::POISON)
 											.And(Selector::type(Bonus::STACK_HEALTH)));
 			if (b)
 				b->val = val;
@@ -1459,21 +1465,17 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs( CGameState *gs )
 			}
 		}
 		vstd::amin(changedStack->firstHPleft, changedStack->MaxHealth());
-		//removal of negative effects
 		if(resurrected)
 		{
-			//removing all features from negative spells
-			const BonusList tmpFeatures = changedStack->getBonusList();
-			//changedStack->bonuses.clear();
-
-			for(Bonus *b : tmpFeatures)
+			//removing all effects from negative spells
+			auto selector = [](const Bonus * b)
 			{
 				const CSpell *s = b->sourceSpell();
-				if(s && s->isNegative())
-				{
-					changedStack->removeBonus(b);
-				}
-			}
+				//Special case: DISRUPTING_RAY is "immune" to dispell
+				//Other even PERMANENT effects can be removed
+				return (s != nullptr) && s->isNegative() && (s->id != SpellID::DISRUPTING_RAY);
+			};
+			changedStack->popBonuses(selector);
 		}
 	}
 }
@@ -1621,18 +1623,8 @@ DLL_LINKAGE void YourTurn::applyGs( CGameState *gs )
 {
 	gs->currentPlayer = player;
 
-	//count days without town
 	auto & playerState = gs->players[player];
-	if(playerState.towns.empty())
-	{
-		if(playerState.daysWithoutCastle)
-			++(*playerState.daysWithoutCastle);
-		else playerState.daysWithoutCastle = 0;
-	}
-	else
-	{
-		playerState.daysWithoutCastle = boost::none;
-	}
+	playerState.daysWithoutCastle = daysWithoutCastle;
 }
 
 DLL_LINKAGE Component::Component(const CStackBasicDescriptor &stack)

+ 3 - 0
lib/VCMI_lib.cbp

@@ -30,6 +30,7 @@
 					<Add option="-lboost_thread$(#boost.libsuffix)" />
 					<Add option="-lboost_chrono$(#boost.libsuffix)" />
 					<Add option="-lboost_locale$(#boost.libsuffix)" />
+					<Add option="-lboost_date_time$(#boost.libsuffix)" />
 					<Add option="-liconv" />
 					<Add option="-ldbghelp" />
 					<Add directory="$(#sdl2.lib)" />
@@ -59,6 +60,7 @@
 					<Add option="-lboost_thread$(#boost.libsuffix)" />
 					<Add option="-lboost_chrono$(#boost.libsuffix)" />
 					<Add option="-lboost_locale$(#boost.libsuffix)" />
+					<Add option="-lboost_date_time$(#boost.libsuffix)" />
 					<Add option="-liconv" />
 					<Add option="-ldbghelp" />
 					<Add directory="$(#sdl2.lib)" />
@@ -89,6 +91,7 @@
 					<Add option="-lboost_thread$(#boost.libsuffix)" />
 					<Add option="-lboost_chrono$(#boost.libsuffix)" />
 					<Add option="-lboost_locale$(#boost.libsuffix)" />
+					<Add option="-lboost_date_time$(#boost.libsuffix)" />
 					<Add option="-liconv" />
 					<Add option="-ldbghelp" />
 					<Add directory="$(#sdl2.lib64)" />

+ 1 - 1
lib/filesystem/AdapterLoaders.h

@@ -42,7 +42,7 @@ public:
 	bool existsResource(const ResourceID & resourceName) const override;
 	std::string getMountPoint() const override;
 	boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
-	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const;
+	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
 
 private:
 	/** A list of files in this map

+ 1 - 1
lib/filesystem/CArchiveLoader.h

@@ -62,7 +62,7 @@ public:
 	std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
 	bool existsResource(const ResourceID & resourceName) const override;
 	std::string getMountPoint() const override;
-	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const;
+	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
 
 private:
 	/**

+ 1 - 1
lib/filesystem/CCompressedStream.h

@@ -116,7 +116,7 @@ private:
 	/**
 	 * Decompresses data to ensure that buffer has newSize bytes or end of stream was reached
 	 */
-	si64 readMore(ui8 * data, si64 size);
+	si64 readMore(ui8 * data, si64 size) override;
 
 	/** The file stream with compressed data. */
 	std::unique_ptr<CInputStream> gzipStream;

+ 43 - 23
lib/mapObjects/CGHeroInstance.cpp

@@ -58,56 +58,49 @@ static int lowestSpeed(const CGHeroInstance * chi)
 
 ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from) const
 {
-	//base move cost
-	unsigned ret = 100;
+	unsigned ret = GameConstants::BASE_MOVEMENT_COST;
 
 	//if there is road both on dest and src tiles - use road movement cost
-    if(dest.roadType != ERoadType::NO_ROAD && from.roadType != ERoadType::NO_ROAD)
+	if(dest.roadType != ERoadType::NO_ROAD && from.roadType != ERoadType::NO_ROAD)
 	{
-        int road = std::min(dest.roadType,from.roadType); //used road ID
+		int road = std::min(dest.roadType,from.roadType); //used road ID
 		switch(road)
 		{
-        case ERoadType::DIRT_ROAD:
+		case ERoadType::DIRT_ROAD:
 			ret = 75;
 			break;
-        case ERoadType::GRAVEL_ROAD:
+		case ERoadType::GRAVEL_ROAD:
 			ret = 65;
 			break;
-        case ERoadType::COBBLESTONE_ROAD:
+		case ERoadType::COBBLESTONE_ROAD:
 			ret = 50;
 			break;
 		default:
-            logGlobal->errorStream() << "Unknown road type: " << road << "... Something wrong!";
+			logGlobal->errorStream() << "Unknown road type: " << road << "... Something wrong!";
 			break;
 		}
 	}
-	else
+	else if(!hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
 	{
-		//FIXME: in H3 presence of Nomad in army will remove terrain penalty for sand. Bonus not implemented in VCMI
-
 		// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
 		// This is clearly bug in H3 however intended behaviour is not clear.
 		// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
 		// will always have best penalty without any influence from player-defined stacks order
 
-		bool nativeArmy = true;
 		for(auto stack : stacks)
 		{
 			int nativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain;
-
-            if (nativeTerrain != -1 && nativeTerrain != from.terType)
+			if(nativeTerrain != -1 && nativeTerrain != from.terType)
 			{
-				nativeArmy = false;
+				ret = VLC->heroh->terrCosts[from.terType];
+				ret -= getSecSkillLevel(SecondarySkill::PATHFINDING) * 25;
+				if(ret < GameConstants::BASE_MOVEMENT_COST)
+					ret = GameConstants::BASE_MOVEMENT_COST;
+
 				break;
 			}
 		}
-		if (!nativeArmy)
-        {
-            ret = VLC->heroh->terrCosts[from.terType];
-            ret-=getSecSkillLevel(SecondarySkill::PATHFINDING)*25;
-            ret = ret < 100 ? 100 : ret;
-        }
- 	}
+	}
 	return ret;
 }
 
@@ -902,6 +895,33 @@ ui32 CGHeroInstance::getSpellBonus(const CSpell * spell, ui32 base, const CStack
 	return base;	
 }
 
+int CGHeroInstance::getEffectLevel(const CSpell * spell) const
+{
+	if(hasBonusOfType(Bonus::MAXED_SPELL, spell->id))
+		return 3;//todo: recheck specialty from where this bonus is. possible bug
+	else
+		return getSpellSchoolLevel(spell);		
+}
+
+int CGHeroInstance::getEffectPower(const CSpell * spell) const
+{
+	return getPrimSkillLevel(PrimarySkill::SPELL_POWER);
+}
+
+int CGHeroInstance::getEnchantPower(const CSpell * spell) const
+{
+	return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION);	
+}
+
+int CGHeroInstance::getEffectValue(const CSpell * spell) const
+{
+	return 0;
+}
+
+const PlayerColor CGHeroInstance::getOwner() const
+{
+	return tempOwner;
+}
 
 bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
 {
@@ -939,7 +959,7 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
         {//hero has this spell in spellbook
             logGlobal->errorStream() << "Banned spell " << spell->name << " in spellbook.";
         }
-        return specificBonus;
+        return specificBonus || schoolBonus || levelBonus;
     }
     else
     {

+ 19 - 5
lib/mapObjects/CGHeroInstance.h

@@ -117,18 +117,18 @@ public:
 	} skillsInfo;
 
 	//int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
-	int getSightRadious() const; //sight distance (should be used if player-owned structure)
+	int getSightRadious() const override; //sight distance (should be used if player-owned structure)
 	//////////////////////////////////////////////////////////////////////////
 
-	int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
-	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
+	int getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
+	void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed
 
 	//////////////////////////////////////////////////////////////////////////
 
 	bool hasSpellbook() const;
 	EAlignment::EAlignment getAlignment() const;
 	const std::string &getBiography() const;
-	bool needsLastStack()const;
+	bool needsLastStack()const override;
 	ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
 	ui32 getLowestCreatureSpeed() const;
 	int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
@@ -177,7 +177,7 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 
-	void setType(si32 ID, si32 subID);
+	void setType(si32 ID, si32 subID) override;
 
 	void initHero();
 	void initHero(HeroTypeID SUBID);
@@ -210,6 +210,20 @@ public:
 	ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
 	ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
 	
+	///default spell school level for effect calculation
+	int getEffectLevel(const CSpell * spell) const override;
+
+	///default spell-power for damage/heal calculation
+	int getEffectPower(const CSpell * spell) const override;
+
+	///default spell-power for timed effects duration
+	int getEnchantPower(const CSpell * spell) const override;
+
+	///damage/heal override(ignores spell configuration, effect level and effect power)
+	int getEffectValue(const CSpell * spell) const override;
+	
+	const PlayerColor getOwner() const override;
+	
 	void deserializationFix();
 
 	void initObj() override;

+ 1 - 1
lib/mapObjects/CGMarket.h

@@ -76,7 +76,7 @@ class DLL_LINKAGE CGUniversity : public CGMarket
 public:
 	std::vector<int> skills; //available skills
 
-	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
+	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
 	void initObj() override;//set skills for trade
 	void onHeroVisit(const CGHeroInstance * h) const override; //open window
 

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -184,7 +184,7 @@ void CGDwelling::updateGuards() const
 	//default condition - creatures are of level 5 or higher
 	for (auto creatureEntry : creatures)
 	{
-		if (VLC->creh->creatures[creatureEntry.second.at(0)]->level >= 5)
+		if (VLC->creh->creatures[creatureEntry.second.at(0)]->level >= 5 && ID != Obj::REFUGEE_CAMP)
 		{
 			guarded = true;
 			break;

+ 7 - 7
lib/mapObjects/CGTownInstance.h

@@ -203,21 +203,21 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 
-	bool passableFor(PlayerColor color) const;
+	bool passableFor(PlayerColor color) const override;
 	//int3 getSightCenter() const override; //"center" tile from which the sight distance is calculated
 	int getSightRadious() const override; //returns sight distance
-	int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
-	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed. Parameter will be cleared
+	int getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
+	void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed. Parameter will be cleared
 	int getMarketEfficiency() const override; //=market count
-	bool allowsTrade(EMarketMode::EMarketMode mode) const;
-	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
+	bool allowsTrade(EMarketMode::EMarketMode mode) const override;
+	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
 
-	void setType(si32 ID, si32 subID);
+	void setType(si32 ID, si32 subID) override;
 	void updateAppearance();
 
 	//////////////////////////////////////////////////////////////////////////
 
-	bool needsLastStack() const;
+	bool needsLastStack() const override;
 	CGTownInstance::EFortLevel fortLevel() const;
 	int hallLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
 	int mageGuildLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol

+ 3 - 3
lib/mapObjects/CQuest.h

@@ -156,7 +156,7 @@ protected:
 class DLL_LINKAGE CGKeymasterTent : public CGKeys
 {
 public:
-	bool wasVisited (PlayerColor player) const;
+	bool wasVisited (PlayerColor player) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -173,9 +173,9 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 
-	void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
+	void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const override;
 	void getRolloverText (MetaString &text, bool onHover) const;
-	bool checkQuest (const CGHeroInstance * h) const;
+	bool checkQuest (const CGHeroInstance * h) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 4 - 6
lib/mapObjects/CommonConstructors.cpp

@@ -313,17 +313,15 @@ void CBankInstanceConstructor::configureObject(CGObjectInstance * object, CRando
 	si32 selectedChance = rng.nextInt(totalChance - 1);
 	//logGlobal->debugStream() << "Selected chance for bank config is " << selectedChance;
 
+	int cumulativeChance = 0;
 	for (auto & node : levels)
 	{
-		if (selectedChance < node["chance"].Float())
+		cumulativeChance += node["chance"].Float();
+		if (selectedChance < cumulativeChance)
 		{
 			 bank->setConfig(generateConfig(node, rng));
+			 break;
 		}
-		else
-		{
-			selectedChance -= node["chance"].Float();
-		}
-
 	}
 }
 

+ 28 - 28
lib/mapObjects/CommonConstructors.h

@@ -39,16 +39,16 @@ protected:
 public:
 	CDefaultObjectTypeHandler(){}
 
-	CGObjectInstance * create(ObjectTemplate tmpl) const
+	CGObjectInstance * create(ObjectTemplate tmpl) const override
 	{
 		return createTyped(tmpl);
 	}
 
-	virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
+	virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override
 	{
 	}
 
-	virtual std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const
+	virtual std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const override
 	{
 		return nullptr;
 	}
@@ -58,24 +58,24 @@ class CObstacleConstructor : public CDefaultObjectTypeHandler<CGObjectInstance>
 {
 public:
 	CObstacleConstructor();
-	bool isStaticObject();
+	bool isStaticObject() override;
 };
 
 class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance>
 {
 	JsonNode filtersJson;
 protected:
-	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
-	void initTypeData(const JsonNode & input);
+	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
+	void initTypeData(const JsonNode & input) override;
 
 public:
 	CFaction * faction;
 	std::map<std::string, LogicalExpression<BuildingID>> filters;
 
 	CTownInstanceConstructor();
-	CGObjectInstance * create(ObjectTemplate tmpl) const;
-	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const;
-	void afterLoadFinalization();
+	CGObjectInstance * create(ObjectTemplate tmpl) const override;
+	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
+	void afterLoadFinalization() override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -88,17 +88,17 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler<CGHeroInstance
 {
 	JsonNode filtersJson;
 protected:
-	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
-	void initTypeData(const JsonNode & input);
+	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
+	void initTypeData(const JsonNode & input) override;
 
 public:
 	CHeroClass * heroClass;
 	std::map<std::string, LogicalExpression<HeroTypeID>> filters;
 
 	CHeroInstanceConstructor();
-	CGObjectInstance * create(ObjectTemplate tmpl) const;
-	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const;
-	void afterLoadFinalization();
+	CGObjectInstance * create(ObjectTemplate tmpl) const override;
+	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
+	void afterLoadFinalization() override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -114,14 +114,14 @@ class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling
 	JsonNode guards;
 
 protected:
-	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
-	void initTypeData(const JsonNode & input);
+	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
+	void initTypeData(const JsonNode & input) override;
 
 public:
 
 	CDwellingInstanceConstructor();
-	CGObjectInstance * create(ObjectTemplate tmpl) const;
-	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const;
+	CGObjectInstance * create(ObjectTemplate tmpl) const override;
+	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 
 	bool producesCreature(const CCreature * crea) const;
 	std::vector<const CCreature *> getProducedCreatures() const;
@@ -163,13 +163,13 @@ public:
 	TPossibleGuards getPossibleGuards() const;
 
 	// These functions should try to evaluate minimal possible/max possible guards to give provide information on possible thread to AI
-	CArmyStructure minGuards() const;
-	CArmyStructure maxGuards() const;
+	CArmyStructure minGuards() const override;
+	CArmyStructure maxGuards() const override;
 
-	bool givesResources() const;
-	bool givesArtifacts() const;
-	bool givesCreatures() const;
-	bool givesSpells() const;
+	bool givesResources() const override;
+	bool givesArtifacts() const override;
+	bool givesCreatures() const override;
+	bool givesSpells() const override;
 };
 
 class CBankInstanceConstructor : public CDefaultObjectTypeHandler<CBank>
@@ -178,7 +178,7 @@ class CBankInstanceConstructor : public CDefaultObjectTypeHandler<CBank>
 
 	JsonVector levels;
 protected:
-	void initTypeData(const JsonNode & input);
+	void initTypeData(const JsonNode & input) override;
 
 public:
 	// all banks of this type will be reset N days after clearing,
@@ -186,10 +186,10 @@ public:
 
 	CBankInstanceConstructor();
 
-	CGObjectInstance *create(ObjectTemplate tmpl) const;
-	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const;
+	CGObjectInstance *create(ObjectTemplate tmpl) const override;
+	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 
-	std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const;
+	std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 2 - 2
lib/mapObjects/MiscObjects.h

@@ -19,7 +19,7 @@ class DLL_LINKAGE CPlayersVisited: public CGObjectInstance
 public:
 	std::set<PlayerColor> players; //players that visited this object
 
-	bool wasVisited(PlayerColor player) const;
+	bool wasVisited(PlayerColor player) const override;
 	bool wasVisited(TeamID team) const;
 	void setPropertyDer(ui8 what, ui32 val) override;
 
@@ -394,7 +394,7 @@ public:
 class CGShipyard : public CGObjectInstance, public IShipyard
 {
 public:
-	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
+	void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed
 	CGShipyard();
 	void onHeroVisit(const CGHeroInstance * h) const override;
 

+ 2 - 2
lib/mapping/MapFormatH3M.h

@@ -50,14 +50,14 @@ public:
 	 *
 	 * @return a unique ptr of the loaded map class
 	 */
-	std::unique_ptr<CMap> loadMap();
+	std::unique_ptr<CMap> loadMap() override;
 
 	/**
 	 * Loads the VCMI/H3 map header.
 	 *
 	 * @return a unique ptr of the loaded map header class
 	 */
-	std::unique_ptr<CMapHeader> loadMapHeader();
+	std::unique_ptr<CMapHeader> loadMapHeader() override;
 
 	/** true if you want to enable the map loader profiler to see how long a specific part took; default=false */
 	static const bool IS_PROFILING_ENABLED;

+ 3 - 3
lib/mapping/MapFormatJson.h

@@ -31,20 +31,20 @@ public:
 	 *
 	 * @return a unique ptr of the loaded map class
 	 */
-	std::unique_ptr<CMap> loadMap();
+	std::unique_ptr<CMap> loadMap() override;
 
 	/**
 	 * Loads the VCMI/Json map header.
 	 *
 	 * @return a unique ptr of the loaded map header class
 	 */
-	std::unique_ptr<CMapHeader> loadMapHeader();
+	std::unique_ptr<CMapHeader> loadMapHeader() override;
 
 	/**
 	 * Modifies supplied map header using Json data
 	 *
 	 */
-	void patchMapHeader(std::unique_ptr<CMapHeader> & header);
+	void patchMapHeader(std::unique_ptr<CMapHeader> & header) override;
 
 private:
 	/**

+ 2 - 2
lib/rmg/CMapGenerator.cpp

@@ -93,8 +93,8 @@ void CMapGenerator::initQuestArtsRemaining()
 {
 	for (auto art : VLC->arth->artifacts)
 	{
-		if (art->aClass == CArtifact::ART_TREASURE && art->constituentOf.empty()) //don't use parts of combined artifacts
-		questArtifacts.push_back(art->id);
+		if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->id) && art->constituentOf.empty()) //don't use parts of combined artifacts
+			questArtifacts.push_back(art->id);
 	}
 }
 

+ 4 - 4
lib/rmg/CRmgTemplateStorage.h

@@ -28,12 +28,12 @@ public:
 
 	const std::map<std::string, CRmgTemplate *> & getTemplates() const;
 
-	std::vector<bool> getDefaultAllowed() const;
-	std::vector<JsonNode> loadLegacyData(size_t dataSize);
+	std::vector<bool> getDefaultAllowed() const override;
+	std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
 
 	/// loads single object into game. Scope is namespace of this object, same as name of source mod
-	virtual void loadObject(std::string scope, std::string name, const JsonNode & data);
-	virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index);
+	virtual void loadObject(std::string scope, std::string name, const JsonNode & data) override;
+	virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
 
 private:
 	CRmgTemplate::CSize parseMapTemplateSize(const std::string & text) const;

+ 10 - 2
lib/rmg/CRmgTemplateZone.cpp

@@ -1506,7 +1506,10 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
 			mine->subID = static_cast<si32>(res);
 			mine->producedResource = res;
 			mine->producedQuantity = mine->defaultResProduction();
-			addCloseObject(mine, 1500);
+			if (!i)
+				addCloseObject(mine, 1500); //only firts one is close
+			else
+				addRequiredObject(mine, 1500);
 		}
 	}
 	for (const auto & res : preciousResources)
@@ -1587,7 +1590,12 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
 		// smallest distance to zone center, greatest distance to nearest object
 		auto isCloser = [this, gen](const int3 & lhs, const int3 & rhs) -> bool
 		{
-			return (this->pos.dist2dSQ(lhs) * 0.5f - gen->getNearestObjectDistance(lhs)) < (this->pos.dist2dSQ(rhs) * 0.5f - gen->getNearestObjectDistance(rhs));
+			float lDist = this->pos.dist2d(lhs);
+			float rDist = this->pos.dist2d(rhs);
+			lDist *= (lDist > 12) ? 10 : 1; //objects within 12 tile radius are preferred (smaller distance rating)
+			rDist *= (rDist > 12) ? 10 : 1;
+
+			return (lDist * 0.5f - std::sqrt(gen->getNearestObjectDistance(lhs))) < (rDist * 0.5f - std::sqrt(gen->getNearestObjectDistance(rhs)));
 		};
 
 		boost::sort (tiles, isCloser);

+ 202 - 77
lib/spells/BattleSpellMechanics.cpp

@@ -13,6 +13,37 @@
 
 #include "../NetPacks.h"
 #include "../BattleState.h"
+#include "../mapObjects/CGHeroInstance.h"
+
+///HealingSpellMechanics
+void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+{
+	EHealLevel healLevel = getHealLevel(parameters.effectLevel);
+	int hpGained = calculateHealedHP(env, parameters, ctx);
+	StacksHealedOrResurrected shr;
+	shr.lifeDrain = false;
+	shr.tentHealing = false;
+
+	const bool resurrect = (healLevel != EHealLevel::HEAL);
+	for(auto & attackedCre : ctx.attackedCres)
+	{
+		StacksHealedOrResurrected::HealInfo hi;
+		hi.stackID = (attackedCre)->ID;		
+		int stackHPgained = parameters.caster->getSpellBonus(owner, hpGained, attackedCre);
+		hi.healedHP = attackedCre->calculateHealedHealthPoints(stackHPgained, resurrect);
+		hi.lowLevelResurrection = (healLevel == EHealLevel::RESURRECT);
+		shr.healedStacks.push_back(hi);
+	}
+	if(!shr.healedStacks.empty())
+		env->sendAndApply(&shr);
+}
+
+int HealingSpellMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, SpellCastContext& ctx) const
+{
+	if(parameters.effectValue != 0)
+		return parameters.effectValue; //Archangel
+	return owner->calculateRawEffectValue(parameters.effectLevel, parameters.effectPower); //???
+}
 
 ///AntimagicMechanics
 void AntimagicMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
@@ -67,7 +98,7 @@ std::set<const CStack *> ChainLightningMechanics::getAffectedStacks(SpellTargeti
 }
 
 ///CloneMechanics
-void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
 	const CStack * clonedStack = nullptr;
 	if(ctx.attackedCres.size())
@@ -101,23 +132,21 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battle
 	env->sendAndApply(&ssp);	
 }
 
-ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
+ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
 {
 	//can't clone already cloned creature
 	if(vstd::contains(obj->state, EBattleStackState::CLONED))
 		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
 	if(obj->cloneID != -1)
 		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-	//TODO: how about stacks casting Clone?
-	//currently Clone casted by stack is assumed Expert level
 	ui8 schoolLevel;
 	if(caster)
 	{
-		schoolLevel = caster->getSpellSchoolLevel(owner);
+		schoolLevel = caster->getEffectLevel(owner);
 	}
 	else
 	{
-		schoolLevel = 3;
+		schoolLevel = 3;//todo: remove
 	}
 
 	if(schoolLevel < 3)
@@ -146,6 +175,11 @@ void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * pac
 	});
 }
 
+HealingSpellMechanics::EHealLevel CureMechanics::getHealLevel(int effectLevel) const
+{
+	return EHealLevel::HEAL;
+}
+
 ///DispellMechanics
 void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
 {
@@ -153,21 +187,35 @@ void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast *
 	doDispell(battle, packet, Selector::sourceType(Bonus::SPELL_EFFECT));
 }
 
-ESpellCastProblem::ESpellCastProblem DispellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
+ESpellCastProblem::ESpellCastProblem DispellMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
 {
-	//DISPELL ignores all immunities, so do not call default
-	std::stringstream cachingStr;
-	cachingStr << "source_" << Bonus::SPELL_EFFECT;
-
-	if(obj->hasBonus(Selector::sourceType(Bonus::SPELL_EFFECT), cachingStr.str()))
 	{
-		return ESpellCastProblem::OK;
+		//just in case
+		if(!obj->alive())
+			return ESpellCastProblem::WRONG_SPELL_TARGET;			
 	}
+	//DISPELL ignores all immunities, except specific absolute immunity 
+	{
+		//SPELL_IMMUNITY absolute case
+		std::stringstream cachingStr;
+		cachingStr << "type_" << Bonus::SPELL_IMMUNITY << "subtype_" << owner->id.toEnum() << "addInfo_1";
+		if(obj->hasBonus(Selector::typeSubtypeInfo(Bonus::SPELL_IMMUNITY, owner->id.toEnum(), 1), cachingStr.str()))
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+	}
+	{
+		std::stringstream cachingStr;
+		cachingStr << "source_" << Bonus::SPELL_EFFECT;
 
+		if(obj->hasBonus(Selector::sourceType(Bonus::SPELL_EFFECT), cachingStr.str()))
+		{
+			return ESpellCastProblem::OK;
+		}		
+	}
 	return ESpellCastProblem::WRONG_SPELL_TARGET;
+	//any other immunities are ignored - do not execute default algorithm
 }
 
-void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
 	DefaultSpellMechanics::applyBattleEffects(env, parameters, ctx);
 
@@ -190,15 +238,15 @@ void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, Batt
 }
 
 ///EarthquakeMechanics
-void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	if(nullptr == parameters.cb->town)
+	if(nullptr == parameters.cb->battleGetDefendedTown())
 	{
 		env->complain("EarthquakeMechanics: not town siege");
 		return;
 	}
 
-	if(CGTownInstance::NONE == parameters.cb->town->fortLevel())
+	if(CGTownInstance::NONE == parameters.cb->battleGetDefendedTown()->fortLevel())
 	{
 		env->complain("EarthquakeMechanics: town has no fort");
 		return;
@@ -277,7 +325,7 @@ void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, B
 	env->sendAndApply(&ca);
 }
 
-ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
+ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
 {
 	if(nullptr == cb->battleGetDefendedTown())
 	{
@@ -288,8 +336,9 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBat
 	{
 		return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 	}
-
-	if(owner->getTargetInfo(0).smart) //TODO: use real spell level
+	
+	CSpell::TargetInfo ti(owner, 0);//TODO: use real spell level
+	if(ti.smart)
 	{
 		//if spell targeting is smart, then only attacker can use it
 		if(cb->playerToSide(player) != 0)
@@ -300,15 +349,15 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBat
 }
 
 ///HypnotizeMechanics
-ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
+ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
 {
-	if(nullptr != caster) //do not resist hypnotize casted after attack, for example
+	//todo: maybe do not resist on passive cast
+	if(nullptr != caster) 
 	{
 		//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
 		ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
 		//apply 'damage' bonus for hypnotize, including hero specialty
-		ui64 maxHealth = caster->getSpellBonus(owner, caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
-			* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), obj);
+		ui64 maxHealth = caster->getSpellBonus(owner, owner->calculateRawEffectValue(caster->getEffectLevel(owner), caster->getEffectPower(owner)), obj);
 		if (subjectHealth > maxHealth)
 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
 	}
@@ -316,9 +365,9 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C
 }
 
 ///ObstacleMechanics
-void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	auto placeObstacle = [&, this](BattleHex pos)
+	auto placeObstacle = [&, this](const BattleHex & pos)
 	{
 		static int obstacleIdToGive =  parameters.cb->obstacles.size()
 									? (parameters.cb->obstacles.back()->uniqueID+1)
@@ -355,8 +404,8 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat
 		obstacle->pos = pos;
 		obstacle->casterSide = parameters.casterSide;
 		obstacle->ID = owner->id;
-		obstacle->spellLevel = parameters.spellLvl;
-		obstacle->casterSpellPower = parameters.usedSpellPower;
+		obstacle->spellLevel = parameters.effectLevel;
+		obstacle->casterSpellPower = parameters.effectPower;
 		obstacle->uniqueID = obstacleIdToGive++;
 
 		BattleObstaclePlaced bop;
@@ -364,6 +413,8 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat
 		env->sendAndApply(&bop);
 	};
 
+	const BattleHex destination = parameters.getFirstDestinationHex();
+
 	switch(owner->id)
 	{
 	case SpellID::QUICKSAND:
@@ -388,12 +439,22 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat
 
 		break;
 	case SpellID::FORCE_FIELD:
-		placeObstacle(parameters.destination);
+		if(!destination.isValid())
+		{
+			env->complain("Invalid destination for FORCE_FIELD");
+			return;
+		}
+		placeObstacle(destination);
 		break;
 	case SpellID::FIRE_WALL:
 		{
+			if(!destination.isValid())
+			{
+				env->complain("Invalid destination for FIRE_WALL");
+				return;
+			}
 			//fire wall is build from multiple obstacles - one fire piece for each affected hex
-			auto affectedHexes = owner->rangeInHexes(parameters.destination, parameters.spellLvl, parameters.casterSide);
+			auto affectedHexes = owner->rangeInHexes(destination, parameters.spellLvl, parameters.casterSide);
 			for(BattleHex hex : affectedHexes)
 				placeObstacle(hex);
 		}
@@ -442,9 +503,9 @@ std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch
 }
 
 ///RemoveObstacleMechanics
-void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.destination, false))
+	if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.getFirstDestinationHex(), false))
 	{
 		ObstaclesRemoved obr;
 		obr.obstacles.insert(obstacleToRemove->uniqueID);
@@ -454,20 +515,34 @@ void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * en
 		env->complain("There's no obstacle to remove!");
 }
 
-///SpecialRisingSpellMechanics
-ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
+HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const
+{
+	//this may be even distinct class
+	if((effectLevel <= 1) && (owner->id == SpellID::RESURRECTION))
+		return EHealLevel::RESURRECT;
+	
+	return EHealLevel::TRUE_RESURRECT;
+}
+
+///SacrificeMechanics
+ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
 {
 	// for sacrifice we have to check for 2 targets (one dead to resurrect and one living to destroy)
 
 	bool targetExists = false;
 	bool targetToSacrificeExists = false;
+	
+	const CGHeroInstance * caster = nullptr; //todo: use ISpellCaster
+	
+	if(cb->battleHasHero(cb->playerToSide(player)))
+		caster = cb->battleGetFightingHero(cb->playerToSide(player));
 
 	for(const CStack * stack : cb->battleGetAllStacks())
 	{
 		//using isImmuneBy directly as this mechanics does not have overridden immunity check
 		//therefore we do not need to check caster and casting mode
 		//TODO: check that we really should check immunity for both stacks
-		ESpellCastProblem::ESpellCastProblem res = owner->isImmuneBy(stack);
+		ESpellCastProblem::ESpellCastProblem res = owner->internalIsImmune(caster, stack);
 		const bool immune =  ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res;
 		const bool casterStack = stack->owner == player;
 
@@ -489,39 +564,57 @@ ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCasted(const CBatt
 }
 
 
-void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	RisingSpellMechanics::applyBattleEffects(env, parameters, ctx);
-
-	if(parameters.selectedStack == parameters.cb->battleActiveStack())
-	//set another active stack than the one removed, or bad things will happen
-	//TODO: make that part of BattleStacksRemoved? what about client update?
+	const CStack * victim = nullptr;
+	if(parameters.destinations.size() == 2)
 	{
-		//makeStackDoNothing(gs->curB->getStack (selectedStack));
-
-		BattleSetActiveStack sas;
-
-		//std::vector<const CStack *> hlp;
-		//battleGetStackQueue(hlp, 1, selectedStack); //next after this one
-
-		//if(hlp.size())
-		//{
-		//	sas.stack = hlp[0]->ID;
-		//}
-		//else
-		//	complain ("No new stack to activate!");
-		sas.stack = parameters.cb->getNextStack()->ID; //why the hell next stack has same ID as current?
-		env->sendAndApply(&sas);
-
+		victim = parameters.destinations[1].stackValue;
 	}
+	else
+	{
+		//todo: remove and report error
+		victim = parameters.selectedStack;
+	}
+	if(nullptr == victim)
+	{
+		env->complain("SacrificeMechanics: No stack to sacrifice");
+		return;
+	}
+	//resurrect target after basic checks
+	RisingSpellMechanics::applyBattleEffects(env, parameters, ctx);
+	//it is safe to remove even active stack
 	BattleStacksRemoved bsr;
-	bsr.stackIDs.insert(parameters.selectedStack->ID); //somehow it works for teleport?
+	bsr.stackIDs.insert(victim->ID);
 	env->sendAndApply(&bsr);
 }
 
+int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, SpellCastContext& ctx) const
+{
+	int res = 0;
+	const CStack * victim = nullptr;
+
+	if(parameters.destinations.size() == 2)
+	{
+		victim = parameters.destinations[1].stackValue;
+	}
+	else
+	{
+		//todo: remove and report error
+		victim = parameters.selectedStack;
+	}
+	if(nullptr == victim)
+	{
+		env->complain("SacrificeMechanics: No stack to sacrifice");
+		return 0;
+	}
+
+	res = (parameters.effectPower + victim->MaxHealth() + owner->getPower(parameters.effectLevel)) * victim->count;
+	return res;
+}
 
 ///SpecialRisingSpellMechanics
-ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
+ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
 {
 	// following does apply to resurrect and animate dead(?) only
 	// for sacrifice health calculation and health limit check don't matter
@@ -529,18 +622,19 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac
 	if(obj->count >= obj->baseAmount)
 		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
 
-	if(caster) //FIXME: Archangels can cast immune stack
-	{
-		auto maxHealth = calculateHealedHP(caster, obj, nullptr);
-		if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature
-			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-	}
+	//FIXME: Archangels can cast immune stack and this should be applied for them and not hero
+//	if(caster) 
+//	{
+//		auto maxHealth = calculateHealedHP(caster, obj, nullptr);
+//		if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature
+//			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+//	}
 
 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);
 }
 
 ///SummonMechanics
-ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
+ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
 {
 	const ui8 side = cb->playerToSide(player);
 
@@ -559,7 +653,7 @@ ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCasted(const CBattleI
 	return ESpellCastProblem::OK;
 }
 
-void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
 	BattleStackAdded bsa;
 	bsa.creID = creatureToSummon;
@@ -570,7 +664,7 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battl
 	//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
 	int percentBonus = parameters.casterHero ? parameters.casterHero->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0;
 
-	bsa.amount = parameters.usedSpellPower
+	bsa.amount = parameters.effectPower
 		* owner->getPower(parameters.spellLvl)
 		* (100 + percentBonus) / 100.0; //new feature - percentage bonus
 	if(bsa.amount)
@@ -580,15 +674,46 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battl
 }
 
 ///TeleportMechanics
-void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	BattleStackMoved bsm;
-	bsm.distance = -1;
-	bsm.stack = parameters.selectedStack->ID;
-	std::vector<BattleHex> tiles;
-	tiles.push_back(parameters.destination);
-	bsm.tilesToMove = tiles;
-	bsm.teleporting = true;
-	env->sendAndApply(&bsm);
+	//todo: check legal teleport
+	if(parameters.destinations.size() == 2)
+	{
+		//first destination creature to move
+		const CStack * target = parameters.destinations[0].stackValue;
+		if(nullptr == target)
+		{
+			env->complain("TeleportMechanics: no stack to teleport");
+			return;
+		}
+		//second destination hex to move to
+		const BattleHex destination = parameters.destinations[1].hexValue;
+		if(!destination.isValid())
+		{
+			env->complain("TeleportMechanics: invalid teleport destination");
+			return;			
+		}
+		BattleStackMoved bsm;
+		bsm.distance = -1;
+		bsm.stack = target->ID;
+		std::vector<BattleHex> tiles;
+		tiles.push_back(destination);
+		bsm.tilesToMove = tiles;
+		bsm.teleporting = true;
+		env->sendAndApply(&bsm);		
+	}
+	else
+	{
+		//todo: remove and report error
+		BattleStackMoved bsm;
+		bsm.distance = -1;
+		bsm.stack = parameters.selectedStack->ID;
+		std::vector<BattleHex> tiles;
+		tiles.push_back(parameters.getFirstDestinationHex());
+		bsm.tilesToMove = tiles;
+		bsm.teleporting = true;
+		env->sendAndApply(&bsm);
+	}		
 }
 
+

+ 40 - 20
lib/spells/BattleSpellMechanics.h

@@ -12,6 +12,23 @@
 
 #include "CDefaultSpellMechanics.h"
 
+class DLL_LINKAGE HealingSpellMechanics : public DefaultSpellMechanics
+{
+public:
+	enum class EHealLevel 
+	{
+		HEAL,
+		RESURRECT,
+		TRUE_RESURRECT
+	};
+
+	HealingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){};	
+protected:
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	virtual int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
+	virtual EHealLevel getHealLevel(int effectLevel) const = 0;
+};
+
 class DLL_LINKAGE AntimagicMechanics : public DefaultSpellMechanics
 {
 public:
@@ -31,44 +48,46 @@ class DLL_LINKAGE CloneMechanics : public DefaultSpellMechanics
 {
 public:
 	CloneMechanics(CSpell * s): DefaultSpellMechanics(s){};
-	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override;
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };
 
-class DLL_LINKAGE CureMechanics : public DefaultSpellMechanics
+class DLL_LINKAGE CureMechanics : public HealingSpellMechanics
 {
 public:
-	CureMechanics(CSpell * s): DefaultSpellMechanics(s){};
+	CureMechanics(CSpell * s): HealingSpellMechanics(s){};
 
 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final;
+	
+	EHealLevel getHealLevel(int effectLevel) const override final;
 };
 
 class DLL_LINKAGE DispellMechanics : public DefaultSpellMechanics
 {
 public:
 	DispellMechanics(CSpell * s): DefaultSpellMechanics(s){};
-	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override;
 
 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final;
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };
 
 class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics
 {
 public:
 	EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){};
-	ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const override;
+	ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override;
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };
 
 class DLL_LINKAGE HypnotizeMechanics : public DefaultSpellMechanics
 {
 public:
 	HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){};
-	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override;
 };
 
 class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics
@@ -77,7 +96,7 @@ public:
 	ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
 
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };
 
 class DLL_LINKAGE WallMechanics : public ObstacleMechanics
@@ -92,15 +111,15 @@ class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics
 public:
 	RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };
 
 ///all rising spells
-class DLL_LINKAGE RisingSpellMechanics : public DefaultSpellMechanics
+class DLL_LINKAGE RisingSpellMechanics : public HealingSpellMechanics
 {
 public:
-	RisingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){};
-
+	RisingSpellMechanics(CSpell * s): HealingSpellMechanics(s){};
+	EHealLevel getHealLevel(int effectLevel) const override;
 };
 
 class DLL_LINKAGE SacrificeMechanics : public RisingSpellMechanics
@@ -108,9 +127,10 @@ class DLL_LINKAGE SacrificeMechanics : public RisingSpellMechanics
 public:
 	SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
 
-	ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const override;
+	ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override;
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;	
 };
 
 ///all rising spells but SACRIFICE
@@ -118,7 +138,7 @@ class DLL_LINKAGE SpecialRisingSpellMechanics : public RisingSpellMechanics
 {
 public:
 	SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){};
-	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override;
 };
 
 class DLL_LINKAGE SummonMechanics : public DefaultSpellMechanics
@@ -126,9 +146,9 @@ class DLL_LINKAGE SummonMechanics : public DefaultSpellMechanics
 public:
 	SummonMechanics(CSpell * s, CreatureID cre): DefaultSpellMechanics(s), creatureToSummon(cre){};
 
-	ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const override;
+	ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override;
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 private:
 	CreatureID creatureToSummon;
 };
@@ -138,5 +158,5 @@ class DLL_LINKAGE TeleportMechanics: public DefaultSpellMechanics
 public:
 	TeleportMechanics(CSpell * s): DefaultSpellMechanics(s){};
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };

+ 161 - 173
lib/spells/CDefaultSpellMechanics.cpp

@@ -139,7 +139,7 @@ void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCa
 			//check for each bonus if it should be removed
 			const bool isSpellEffect = Selector::sourceType(Bonus::SPELL_EFFECT)(b);
 			const int spellID = isSpellEffect ? b->sid : -1;
-
+			//No exceptions, ANY spell can be countered, even if it can`t be dispelled.
 			return isSpellEffect && vstd::contains(owner->counteredSpells, spellID);
 		});
 	}
@@ -223,29 +223,35 @@ ESpellCastResult DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnv
 
 void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
 {
+	logGlobal->debugStream() << "Started spell cast. Spell: "<<owner->name<<"; mode:"<<parameters.mode;
+	
+	if(nullptr == parameters.caster)
+	{
+		env->complain("No spell-caster provided.");
+		return;		
+	}
+	
 	BattleSpellCast sc;
-	sc.side = parameters.casterSide;
-	sc.id = owner->id;
-	sc.skill = parameters.spellLvl;
-	sc.tile = parameters.destination;
-	sc.dmgToDisplay = 0;
-	sc.castByHero = nullptr != parameters.casterHero;
-	sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
-	sc.manaGained = 0;
-
+	prepareBattleCast(parameters, sc);
+	
+	//check it there is opponent hero
+	const ui8 otherSide = 1-parameters.casterSide;
+	const CGHeroInstance * otherHero = nullptr;
+	if(parameters.cb->battleHasHero(otherSide))
+		otherHero = parameters.cb->battleGetFightingHero(otherSide);
 	int spellCost = 0;
 
 	//calculate spell cost
-	if(parameters.casterHero)
+	if(parameters.mode == ECastingMode::HERO_CASTING)
 	{
 		spellCost = parameters.cb->battleGetSpellCost(owner, parameters.casterHero);
 
-		if(parameters.secHero && parameters.mode == ECastingMode::HERO_CASTING) //handle mana channel
-		{
+		if(nullptr != otherHero) //handle mana channel
+		{			
 			int manaChannel = 0;
 			for(const CStack * stack : parameters.cb->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow?
 			{
-				if(stack->owner == parameters.secHero->tempOwner)
+				if(stack->owner == otherHero->tempOwner)
 				{
 					vstd::amax(manaChannel, stack->valOfBonuses(Bonus::MANA_CHANNELING));
 				}
@@ -253,53 +259,36 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
 			sc.manaGained = (manaChannel * spellCost) / 100;
 		}
 	}
+	logGlobal->debugStream() << "spellCost: " << spellCost;
 
 	//calculating affected creatures for all spells
 	//must be vector, as in Chain Lightning order matters
 	std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
 
-	auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.destination, parameters.casterHero);
+	auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.getFirstDestinationHex(), parameters.caster);
 	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
 
+	logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks";
+
 	std::vector <const CStack*> reflected;//for magic mirror
-	
-	//checking if creatures resist
-	//resistance/reflection is applied only to negative spells
-	if(owner->isNegative())
+	//checking if creatures resist	
+	handleResistance(env, attackedCres, sc);
+	//it is actual spell and can be reflected to single target, no recurrence
+	const bool tryMagicMirror = owner->isNegative() && owner->level && owner->getLevelInfo(0).range == "0";	
+	if(tryMagicMirror)
 	{
-		//it is actual spell and can be reflected to single target, no recurrence
-		const bool tryMagicMirror = parameters.mode != ECastingMode::MAGIC_MIRROR && owner->level && owner->getLevelInfo(0).range == "0";
-		std::vector <const CStack*> resisted;
 		for(auto s : attackedCres)
 		{
-			//magic resistance
-			const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
-
-			if(env->getRandomGenerator().nextInt(99) < prob)
-			{
-				resisted.push_back(s);
-			}
-			//magic mirror
-			if(tryMagicMirror)
-			{
-				const int mirrorChance = (s)->valOfBonuses(Bonus::MAGIC_MIRROR);
-				if(env->getRandomGenerator().nextInt(99) < mirrorChance)
-					reflected.push_back(s);
-			}
+			const int mirrorChance = (s)->valOfBonuses(Bonus::MAGIC_MIRROR);
+			if(env->getRandomGenerator().nextInt(99) < mirrorChance)
+				reflected.push_back(s);
 		}
 
-		vstd::erase_if(attackedCres, [&resisted, reflected](const CStack * s)
+		vstd::erase_if(attackedCres, [&reflected](const CStack * s)
 		{
-			return vstd::contains(resisted, s) || vstd::contains(reflected, s);
+			return vstd::contains(reflected, s);
 		});
 
-		for(auto s : resisted)
-		{
-			BattleSpellCast::CustomEffect effect;
-			effect.effect = 78;
-			effect.stack = s->ID;
-			sc.customEffects.push_back(effect);
-		}
 		for(auto s : reflected)
 		{
 			BattleSpellCast::CustomEffect effect;
@@ -316,13 +305,12 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
 
 	StacksInjured si;
 	SpellCastContext ctx(attackedCres, sc, si);
-
 	applyBattleEffects(env, parameters, ctx);
 
 	env->sendAndApply(&sc);
 
 	//spend mana
-	if(parameters.casterHero)
+	if(parameters.mode == ECastingMode::HERO_CASTING)
 	{
 		SetMana sm;
 		sm.absolute = false;
@@ -334,9 +322,9 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
 
 		if(sc.manaGained > 0)
 		{
-			assert(parameters.secHero);
+			assert(otherHero);
 
-			sm.hid = parameters.secHero->id;
+			sm.hid = otherHero->id;
 			sm.val = sc.manaGained;
 			env->sendAndApply(&sm);
 		}
@@ -358,7 +346,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
 		ssp.absolute = false;
 		env->sendAndApply(&ssp);
 	}
-
+	logGlobal->debugStream() << "Finished spell cast. Spell: "<<owner->name<<"; mode:"<<parameters.mode;
 	//Magic Mirror effect
 	for(auto & attackedCre : reflected)
 	{
@@ -373,18 +361,17 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
 		{
 			int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position;
 
-			BattleSpellCastParameters mirrorParameters = parameters;
+			BattleSpellCastParameters mirrorParameters(parameters.cb, attackedCre, owner);
 			mirrorParameters.spellLvl = 0;
-			mirrorParameters.casterSide = 1-parameters.casterSide;
-			mirrorParameters.casterColor = (attackedCre)->owner;
-			mirrorParameters.casterHero = nullptr;
-			mirrorParameters.destination = targetHex;
-			mirrorParameters.secHero = parameters.casterHero;
+			mirrorParameters.aimToHex(targetHex);
 			mirrorParameters.mode = ECastingMode::MAGIC_MIRROR;
-			mirrorParameters.casterStack = (attackedCre);
 			mirrorParameters.selectedStack = nullptr;
-
-			battleCast(env, mirrorParameters);
+			mirrorParameters.spellLvl = parameters.spellLvl;
+			mirrorParameters.effectLevel = parameters.effectLevel;
+			mirrorParameters.effectPower = parameters.effectPower;
+			mirrorParameters.effectValue = parameters.effectValue;
+			mirrorParameters.enchantPower = parameters.enchantPower;
+			castMagicMirror(env, mirrorParameters);
 		}
 	}
 }
@@ -486,74 +473,21 @@ void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & log
 	}	
 }
 
-int DefaultSpellMechanics::calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const
-{
-	if(caster == nullptr)
-	{
-		if (!usedSpellPower)
-			return 3; //default duration of all creature spells
-		else
-			return usedSpellPower; //use creature spell power			
-	}
-	else
-		return caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + caster->valOfBonuses(Bonus::SPELL_DURATION);
-}
-
-ui32 DefaultSpellMechanics::calculateHealedHP(const CGHeroInstance* caster, const CStack* stack, const CStack* sacrificedStack) const
-{
-	int healedHealth;
-
-	if(!owner->isHealingSpell())
-	{
-		logGlobal->errorStream() << "calculateHealedHP called for nonhealing spell "<< owner->name;
-		return 0;
-	}
-
-	const int spellPowerSkill = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
-	const int levelPower = owner->getPower(caster->getSpellSchoolLevel(owner));
-
-	if (owner->id == SpellID::SACRIFICE && sacrificedStack)
-		healedHealth = (spellPowerSkill + sacrificedStack->MaxHealth() + levelPower) * sacrificedStack->count;
-	else
-		healedHealth = spellPowerSkill * owner->power + levelPower; //???
-	healedHealth = caster->getSpellBonus(owner, healedHealth, stack);
-	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (owner->isRisingSpell() ? stack->baseAmount * stack->MaxHealth() : 0));
-}
-
-
-void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	int effectLevel = parameters.spellLvl;
-	{
-		//MAXED_SPELL bonus.
-		if(parameters.casterHero != nullptr)
-			if(parameters.casterHero->hasBonusOfType(Bonus::MAXED_SPELL, owner->id))
-				effectLevel = 3;
-	}
-
 	//applying effects
 	if(owner->isOffensiveSpell())
 	{
-		int spellDamage = 0;
-		if(parameters.casterStack && parameters.mode != ECastingMode::MAGIC_MIRROR)
-		{
-			int unitSpellPower = parameters.casterStack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, owner->id.toEnum());
-			if(unitSpellPower)
-				ctx.sc.dmgToDisplay = spellDamage = parameters.casterStack->count * unitSpellPower; //TODO: handle immunities
-			else //Faerie Dragon
-			{
-				parameters.usedSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * parameters.casterStack->count / 100;
-				ctx.sc.dmgToDisplay = 0;
-			}
-		}
+		int spellDamage = parameters.effectValue;
+		
 		int chainLightningModifier = 0;
 		for(auto & attackedCre : ctx.attackedCres)
 		{
 			BattleStackAttacked bsa;
-			if(spellDamage)
-				bsa.damageAmount = spellDamage >> chainLightningModifier;
+			if(spellDamage != 0)
+				bsa.damageAmount = owner->adjustRawDamage(parameters.caster, attackedCre, spellDamage) >> chainLightningModifier;
 			else
-				bsa.damageAmount =  owner->calculateDamage(parameters.casterHero, attackedCre, effectLevel, parameters.usedSpellPower) >> chainLightningModifier;
+				bsa.damageAmount = owner->calculateDamage(parameters.caster, attackedCre, parameters.effectLevel, parameters.effectPower) >> chainLightningModifier;
 
 			ctx.sc.dmgToDisplay += bsa.damageAmount;
 
@@ -572,19 +506,14 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
 
 	if(owner->hasEffects())
 	{
-		int stackSpellPower = 0;
-		if(parameters.casterStack && parameters.mode != ECastingMode::MAGIC_MIRROR)
-		{
-			stackSpellPower =  parameters.casterStack->valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
-		}
 		SetStackEffect sse;
 		//get default spell duration (spell power with bonuses for heroes)
-		int duration = calculateDuration(parameters.casterHero, stackSpellPower ? stackSpellPower : parameters.usedSpellPower);		
+		int duration = parameters.enchantPower;
 		//generate actual stack bonuses
 		{
 			int maxDuration = 0;
 			std::vector<Bonus> tmp;
-			owner->getEffects(tmp, effectLevel);
+			owner->getEffects(tmp, parameters.effectLevel);
 			for(Bonus& b : tmp)
 			{
 				//use configured duration if present
@@ -664,49 +593,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
 		if(!sse.stacks.empty())
 			env->sendAndApply(&sse);
 
-	}
-
-	if(owner->isHealingSpell())
-	{
-		int hpGained = 0;
-		if(parameters.casterStack)
-		{
-			int unitSpellPower = parameters.casterStack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, owner->id.toEnum());
-			if(unitSpellPower)
-				hpGained = parameters.casterStack->count * unitSpellPower; //Archangel
-			else //Faerie Dragon-like effect - unused so far
-				parameters.usedSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * parameters.casterStack->count / 100;
-		}
-		StacksHealedOrResurrected shr;
-		shr.lifeDrain = false;
-		shr.tentHealing = false;
-		for(auto & attackedCre : ctx.attackedCres)
-		{
-			StacksHealedOrResurrected::HealInfo hi;
-			hi.stackID = (attackedCre)->ID;
-			if (parameters.casterStack) //casted by creature
-			{
-				const bool resurrect = owner->isRisingSpell();
-				if (hpGained)
-				{
-					//archangel
-					hi.healedHP = std::min<ui32>(hpGained, attackedCre->MaxHealth() - attackedCre->firstHPleft + (resurrect ? attackedCre->baseAmount * attackedCre->MaxHealth() : 0));
-				}
-				else
-				{
-					//any typical spell (commander's cure or animate dead)
-					int healedHealth = parameters.usedSpellPower * owner->power + owner->getPower(effectLevel);
-					hi.healedHP = std::min<ui32>(healedHealth, attackedCre->MaxHealth() - attackedCre->firstHPleft + (resurrect ? attackedCre->baseAmount * attackedCre->MaxHealth() : 0));
-				}
-			}
-			else
-				hi.healedHP = calculateHealedHP(parameters.casterHero, attackedCre, parameters.selectedStack); //Casted by hero
-			hi.lowLevelResurrection = (effectLevel <= 1) && (owner->id != SpellID::ANIMATE_DEAD);
-			shr.healedStacks.push_back(hi);
-		}
-		if(!shr.healedStacks.empty())
-			env->sendAndApply(&shr);
-	}
+	}	
 }
 
 std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
@@ -849,24 +736,125 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting
 	return attackedCres;
 }
 
-ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
+ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
 {
 	//no problems by default, this method is for spell-specific problems	
 	return ESpellCastProblem::OK;
 }
 
-
-ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
+ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
 {
 	//by default use general algorithm
-	return owner->isImmuneBy(obj);
+	return owner->internalIsImmune(caster, obj);
 }
 
 void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const
 {
+	auto localSelector = [](const Bonus * bonus)
+	{
+		const CSpell * sourceSpell = bonus->sourceSpell();
+		if(sourceSpell != nullptr)
+		{
+			//Special case: DISRUPTING_RAY is "immune" to dispell
+			//Other even PERMANENT effects can be removed (f.e. BIND)						
+			if(sourceSpell->id == SpellID::DISRUPTING_RAY)
+				return false;
+		}
+		return true;
+	};
 	for(auto stackID : packet->affectedCres)
 	{
 		CStack *s = battle->getStack(stackID);
-		s->popBonuses(selector);
+		s->popBonuses(CSelector(localSelector).And(selector));
+	}	
+}
+
+void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, BattleSpellCastParameters& parameters) const
+{
+	logGlobal->debugStream() << "Started spell cast. Spell: "<<owner->name<<"; mode: MAGIC_MIRROR";
+	if(parameters.mode != ECastingMode::MAGIC_MIRROR)
+	{
+		env->complain("MagicMirror: invalid mode");
+		return;
+	}
+	BattleHex destination = parameters.getFirstDestinationHex();
+	if(!destination.isValid())
+	{
+		env->complain("MagicMirror: invalid destination");
+		return;		
+	}
+
+	BattleSpellCast sc;
+	prepareBattleCast(parameters, sc);
+	
+	//calculating affected creatures for all spells
+	//must be vector, as in Chain Lightning order matters
+	std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
+
+	auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, destination, parameters.caster);
+	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
+
+	logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks";
+
+	handleResistance(env, attackedCres, sc);
+
+	for(auto cre : attackedCres)
+	{
+		sc.affectedCres.insert(cre->ID);
+	}
+	
+	StacksInjured si;
+	SpellCastContext ctx(attackedCres, sc, si);
+	applyBattleEffects(env, parameters, ctx);
+
+	env->sendAndApply(&sc);
+	if(!si.stacks.empty()) //after spellcast info shows
+		env->sendAndApply(&si);	
+	logGlobal->debugStream() << "Finished spell cast. Spell: "<<owner->name<<"; mode: MAGIC_MIRROR";
+}
+
+void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, std::vector<const CStack* >& attackedCres, BattleSpellCast& sc) const
+{
+	//checking if creatures resist
+	//resistance/reflection is applied only to negative spells
+	if(owner->isNegative())
+	{
+		std::vector <const CStack*> resisted;
+		for(auto s : attackedCres)
+		{
+			//magic resistance
+			const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
+
+			if(env->getRandomGenerator().nextInt(99) < prob)
+			{
+				resisted.push_back(s);
+			}
+		}
+
+		vstd::erase_if(attackedCres, [&resisted](const CStack * s)
+		{
+			return vstd::contains(resisted, s);
+		});
+
+		for(auto s : resisted)
+		{
+			BattleSpellCast::CustomEffect effect;
+			effect.effect = 78;
+			effect.stack = s->ID;
+			sc.customEffects.push_back(effect);
+		}		
 	}	
 }
+
+void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& parameters, BattleSpellCast& sc) const
+{
+	sc.side = parameters.casterSide;
+	sc.id = owner->id;
+	sc.skill = parameters.spellLvl;
+	sc.tile = parameters.getFirstDestinationHex();
+	sc.dmgToDisplay = 0;
+	sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING;
+	sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
+	sc.manaGained = 0;	
+}
+

+ 11 - 10
lib/spells/CDefaultSpellMechanics.h

@@ -19,7 +19,9 @@ class StacksInjured;
 struct SpellCastContext
 {
 	SpellCastContext(std::vector<const CStack *> & attackedCres, BattleSpellCast & sc, StacksInjured & si):
-		attackedCres(attackedCres), sc(sc), si(si){};
+		attackedCres(attackedCres), sc(sc), si(si)	
+	{
+	};
 	std::vector<const CStack *> & attackedCres;
 	BattleSpellCast & sc;
 	StacksInjured & si;
@@ -40,26 +42,25 @@ public:
 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override;
 	std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override;
 
-	ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const override;
+	ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override;
 	
-	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override;
 
 	virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;
 	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final;
-	void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override;
+	void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override final;
 
 	void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet, 
 		const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override;	
 protected:
-	virtual void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
-
-	virtual int calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const;
-
-	///calculate healed HP for all spells casted by hero
-	ui32 calculateHealedHP(const CGHeroInstance * caster, const CStack * stack, const CStack * sacrificedStack) const;
+	virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
 
 	///actual adventure cast implementation
 	virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const;
 	
 	void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const;
+private:
+	void castMagicMirror(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const;
+	void handleResistance(const SpellCastEnvironment * env, std::vector<const CStack*> & attackedCres, BattleSpellCast & sc) const;
+	void prepareBattleCast(const BattleSpellCastParameters & parameters, BattleSpellCast & sc) const;
 };

+ 87 - 70
lib/spells/CSpellHandler.cpp

@@ -23,10 +23,11 @@
 #include "../CModHandler.h"
 #include "../StringConstants.h"
 
-#include "../mapObjects/CGHeroInstance.h"
 #include "../BattleState.h"
 #include "../CBattleCallback.h"
-#include "../CGameState.h"
+#include "../CGameState.h" //todo: remove
+
+#include "../NetPacks.h" //todo: remove
 
 #include "ISpellMechanics.h"
 
@@ -71,13 +72,6 @@ namespace SpellConfig
 	};
 }
 
-BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo* cb)
-	: spellLvl(0), destination(BattleHex::INVALID), casterSide(0),casterColor(PlayerColor::CANNOT_DETERMINE),casterHero(nullptr), secHero(nullptr),
-	usedSpellPower(0),mode(ECastingMode::HERO_CASTING), casterStack(nullptr), selectedStack(nullptr), cb(cb)
-{
-
-}
-
 ///CSpell::LevelInfo
 CSpell::LevelInfo::LevelInfo()
 	:description(""),cost(0),power(0),AIValue(0),smartTarget(true), clearTarget(false), clearAffected(false), range("0")
@@ -121,9 +115,13 @@ bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastP
 }
 
 void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
-{
+{	
 	assert(env);
-
+	if(parameters.destinations.size()<1)
+	{
+		env->complain("Spell must have at least one destination");
+		return;
+	}
 	mechanics->battleCast(env, parameters);
 }
 
@@ -140,52 +138,15 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
 
 ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const
 {
-	ui32 ret = 0; //value to return
-
 	//check if spell really does damage - if not, return 0
 	if(!isDamageSpell())
 		return 0;
-
-	ret = usedSpellPower * power;
-	ret += getPower(spellSchoolLevel);
-
-	//affected creature-specific part
-	if(nullptr != affectedCreature)
-	{
-		//applying protections - when spell has more then one elements, only one protection should be applied (I think)
-		forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
-		{
-			if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id))
-			{
-				ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id);
-				ret /= 100;
-				stop = true;//only bonus from one school is used
-			}
-		});
-
-		//general spell dmg reduction
-		if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1))
-		{
-			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
-			ret /= 100;
-		}
-
-		//dmg increasing
-		if(affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, id))
-		{
-			ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, id.toEnum());
-			ret /= 100;
-		}
-	}
-	
-	if(nullptr != caster) //todo: make sure that caster always present	
-		ret = caster->getSpellBonus(this, ret, affectedCreature);
-	return ret;
+	return adjustRawDamage(caster, affectedCreature, calculateRawEffectValue(spellSchoolLevel, usedSpellPower));	
 }
 
-ESpellCastProblem::ESpellCastProblem CSpell::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
+ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
 {
-	return mechanics->canBeCasted(cb, player);
+	return mechanics->canBeCast(cb, player);
 }
 
 std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
@@ -193,7 +154,7 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
 	return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes);
 }
 
-std::set<const CStack *> CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster) const
+std::set<const CStack *> CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const ISpellCaster * caster) const
 {
 	ISpellMechanics::SpellTargetingContext ctx(this, cb,mode,casterColor,spellLvl,destination);
 
@@ -217,12 +178,6 @@ CSpell::ETargetType CSpell::getTargetType() const
 	return targetType;
 }
 
-CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
-{
-	TargetInfo info(this, level);
-	return info;
-}
-
 void CSpell::forEachSchool(const std::function<void(const SpellSchoolInfo &, bool &)>& cb) const
 {
 	bool stop = false;
@@ -351,7 +306,7 @@ void CSpell::getEffects(std::vector<Bonus> & lst, const int level) const
 	}
 }
 
-ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * caster, ECastingMode::ECastingMode mode, BattleHex destination) const
+ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const
 {
 	// Get all stacks at destination hex. only alive if not rising spell
 	TStacks stacks = cb->battleGetStacksIf([=](const CStack * s){
@@ -402,7 +357,48 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallbac
 	return ESpellCastProblem::OK;
 }
 
-ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) const
+int CSpell::adjustRawDamage(const ISpellCaster * caster, const CStack * affectedCreature, int rawDamage) const
+{
+	int ret = rawDamage;
+	//affected creature-specific part
+	if(nullptr != affectedCreature)
+	{
+		//applying protections - when spell has more then one elements, only one protection should be applied (I think)
+		forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
+		{
+			if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id))
+			{
+				ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id);
+				ret /= 100;
+				stop = true;//only bonus from one school is used
+			}
+		});
+
+		//general spell dmg reduction
+		if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1))
+		{
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
+			ret /= 100;
+		}
+
+		//dmg increasing
+		if(affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, id))
+		{
+			ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, id.toEnum());
+			ret /= 100;
+		}
+	}
+	if(caster != nullptr)
+		ret = caster->getSpellBonus(this, ret, affectedCreature);
+	return ret;
+}
+
+int CSpell::calculateRawEffectValue(int effectLevel, int effectPower) const
+{
+	return effectPower * power + getPower(effectLevel);	
+}
+
+ESpellCastProblem::ESpellCastProblem CSpell::internalIsImmune(const ISpellCaster * caster, const CStack *obj) const
 {
 	//todo: use new bonus API
 	//1. Check absolute limiters
@@ -419,25 +415,46 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj)
 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
 	}
 
-	//spell-based spell immunity (only ANTIMAGIC in OH3) is treated as absolute
-	std::stringstream cachingStr;
-	cachingStr << "type_" << Bonus::LEVEL_SPELL_IMMUNITY << "source_" << Bonus::SPELL_EFFECT;
+	{
+		//spell-based spell immunity (only ANTIMAGIC in OH3) is treated as absolute
+		std::stringstream cachingStr;
+		cachingStr << "type_" << Bonus::LEVEL_SPELL_IMMUNITY << "source_" << Bonus::SPELL_EFFECT;
 
-	TBonusListPtr levelImmunitiesFromSpell = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY).And(Selector::sourceType(Bonus::SPELL_EFFECT)), cachingStr.str());	
+		TBonusListPtr levelImmunitiesFromSpell = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY).And(Selector::sourceType(Bonus::SPELL_EFFECT)), cachingStr.str());	
 
-	if(levelImmunitiesFromSpell->size() > 0  &&  levelImmunitiesFromSpell->totalValue() >= level  &&  level)
-	{
-		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+		if(levelImmunitiesFromSpell->size() > 0  &&  levelImmunitiesFromSpell->totalValue() >= level  &&  level)
+		{
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+		}
 	}
+	{
+		//SPELL_IMMUNITY absolute case
+		std::stringstream cachingStr;
+		cachingStr << "type_" << Bonus::SPELL_IMMUNITY << "subtype_" << id.toEnum() << "addInfo_1";
+		if(obj->hasBonus(Selector::typeSubtypeInfo(Bonus::SPELL_IMMUNITY, id.toEnum(), 1), cachingStr.str()))
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+	}	
 
 	//check receptivity
 	if (isPositive() && obj->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
 		return ESpellCastProblem::OK;
 		
 	//3. Check negation
-	//FIXME: Orb of vulnerability mechanics is not such trivial
-	if(obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES)) //Orb of vulnerability
+	//Orb of vulnerability
+	//FIXME: Orb of vulnerability mechanics is not such trivial (issue 1791)
+	const bool battleWideNegation = obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES, 0);
+	const bool heroNegation = obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES, 1);
+	//anyone can cast on artifact holder`s stacks
+	if(heroNegation) 
 		return ESpellCastProblem::NOT_DECIDED;
+	//this stack is from other player
+	//todo: check that caster is always present (not trivial is this case)
+	//todo: NEGATE_ALL_NATURAL_IMMUNITIES special cases: dispell, chain lightning
+	else if(battleWideNegation && caster)
+	{
+		if(obj->owner != caster->getOwner())
+			return ESpellCastProblem::NOT_DECIDED;
+	}
 
 	//4. Check negatable limit
 	for(auto b : limiters)
@@ -491,7 +508,7 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj)
 	return ESpellCastProblem::NOT_DECIDED;
 }
 
-ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
+ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
 {
 	const auto immuneResult = mechanics->isImmuneByStack(caster,obj);
 

+ 21 - 55
lib/spells/CSpellHandler.h

@@ -30,6 +30,9 @@ struct BattleSpellCast;
 class CGameInfoCallback;
 class CRandomGenerator;
 class CMap;
+struct AdventureSpellCastParameters;
+struct BattleSpellCastParameters;
+class SpellCastEnvironment;
 
 struct SpellSchoolInfo
 {
@@ -41,45 +44,6 @@ struct SpellSchoolInfo
 	Bonus::BonusType knoledgeBonus;
 };
 
-///callback to be provided by server
-class DLL_LINKAGE SpellCastEnvironment
-{
-public:
-	virtual ~SpellCastEnvironment(){};
-	virtual void sendAndApply(CPackForClient * info) const = 0;
-
-	virtual CRandomGenerator & getRandomGenerator() const = 0;
-	virtual void complain(const std::string & problem) const = 0;
-
-	virtual const CMap * getMap() const = 0;
-	virtual const CGameInfoCallback * getCb() const = 0;
-
-	virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const =0;	//TODO: remove
-};
-
-///helper struct
-struct DLL_LINKAGE BattleSpellCastParameters
-{
-public:
-	BattleSpellCastParameters(const BattleInfo * cb);
-	int spellLvl;
-	BattleHex destination;
-	ui8 casterSide;
-	PlayerColor casterColor;
-	const CGHeroInstance * casterHero; //deprecated
-	const CGHeroInstance * secHero;
-	int usedSpellPower;
-	ECastingMode::ECastingMode mode;
-	const CStack * casterStack;
-	const CStack * selectedStack;
-	const BattleInfo * cb;
-};
-
-struct DLL_LINKAGE AdventureSpellCastParameters
-{
-	const CGHeroInstance * caster;
-	int3 pos;
-};
 
 enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM};
 
@@ -136,10 +100,10 @@ public:
 		///displayed on caster.
 		TAnimationQueue cast;
 
-		///displayed on target hex. If spell was casted with no target selection displayed on entire battlefield (f.e. ARMAGEDDON)
+		///displayed on target hex. If spell was cast with no target selection displayed on entire battlefield (f.e. ARMAGEDDON)
 		TAnimationQueue hit;
 
-		///displayed "between" caster and (first) target. Ignored if spell was casted with no target selection.
+		///displayed "between" caster and (first) target. Ignored if spell was cast with no target selection.
 		///use selectProjectile to access
 		std::vector<ProjectileInfo> projectile;
 
@@ -188,7 +152,7 @@ public:
 	enum ETargetType {NO_TARGET, CREATURE, OBSTACLE, LOCATION};
 	enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
 
-	struct TargetInfo
+	struct DLL_LINKAGE TargetInfo
 	{
 		ETargetType type;
 		bool smart;
@@ -231,8 +195,6 @@ public:
 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr ) const; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret)
 	ETargetType getTargetType() const; //deprecated
 
-	CSpell::TargetInfo getTargetInfo(const int level) const;
-
 	bool isCombatSpell() const;
 	bool isAdventureSpell() const;
 	bool isCreatureAbility() const;
@@ -251,17 +213,12 @@ public:
 	bool hasEffects() const;
 	void getEffects(std::vector<Bonus> &lst, const int level) const;
 
-	///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc.
-	ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * caster, ECastingMode::ECastingMode mode, BattleHex destination) const;
-
-	//internal, for use only by Mechanics classes
-	ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
 
 	///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
 	ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
 
 	///selects from allStacks actually affected stacks
-	std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster = nullptr) const;
+	std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const ISpellCaster * caster) const;
 
 	si32 getCost(const int skillLevel) const;
 
@@ -310,10 +267,13 @@ public:
 	///internal interface (for callbacks)
 	
 	///Checks general but spell-specific problems for all casting modes. Use only during battle.
-	ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const;
+	ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const;
 
+	///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc.
+	ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const;
+	
 	///checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc.
-	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const;
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const;
 public:
 	///Server logic. Has write access to GameState via packets.
 	///May be executed on client side by (future) non-cheat-proof scripts.
@@ -328,11 +288,17 @@ public:
 	///implementation of BattleSpellCast applying
 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
 
-public:
-	///Client logic.
-	
+public://Client logic.
 	void prepareBattleLog(const CBattleInfoCallback * cb, const BattleSpellCast * packet, std::vector<std::string> & logLines) const;
 
+public://internal, for use only by Mechanics classes
+	///applies caster`s secondary skills and affectedCreature`s to raw damage
+	int adjustRawDamage(const ISpellCaster * caster, const CStack * affectedCreature, int rawDamage) const;
+	///returns raw damage or healed HP
+	int calculateRawEffectValue(int effectLevel, int effectPower) const;		
+	///generic immunity calculation
+	ESpellCastProblem::ESpellCastProblem internalIsImmune(const ISpellCaster * caster, const CStack *obj) const;
+
 private:
 	void setIsOffensive(const bool val);
 	void setIsRising(const bool val);

+ 8 - 7
lib/spells/CreatureSpellMechanics.cpp

@@ -16,17 +16,18 @@
 #include "../BattleState.h"
 
 ///AcidBreathDamageMechanics
-void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
+	//todo: this should be effectValue
 	//calculating dmg to display
-	ctx.sc.dmgToDisplay = parameters.usedSpellPower;
+	ctx.sc.dmgToDisplay = parameters.effectPower;
 
 	for(auto & attackedCre : ctx.attackedCres) //no immunities
 	{
 		BattleStackAttacked bsa;
 		bsa.flags |= BattleStackAttacked::SPELL_EFFECT;
 		bsa.spellID = owner->id;
-		bsa.damageAmount = parameters.usedSpellPower; //damage times the number of attackers
+		bsa.damageAmount = parameters.effectPower; //damage times the number of attackers
 		bsa.stackAttacked = (attackedCre)->ID;
 		bsa.attackerID = -1;
 		(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
@@ -35,10 +36,10 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment *
 }
 
 ///DeathStareMechanics
-void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
+void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
 	//calculating dmg to display
-	ctx.sc.dmgToDisplay = parameters.usedSpellPower;
+	ctx.sc.dmgToDisplay = parameters.effectPower;
 	if(!ctx.attackedCres.empty())
 		vstd::amin(ctx.sc.dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack
 
@@ -47,7 +48,7 @@ void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, B
 		BattleStackAttacked bsa;
 		bsa.flags |= BattleStackAttacked::SPELL_EFFECT;
 		bsa.spellID = owner->id;
-		bsa.damageAmount = parameters.usedSpellPower * (attackedCre)->valOfBonuses(Bonus::STACK_HEALTH);
+		bsa.damageAmount = parameters.effectPower * (attackedCre)->valOfBonuses(Bonus::STACK_HEALTH);//todo: move here all DeathStare calculation 
 		bsa.stackAttacked = (attackedCre)->ID;
 		bsa.attackerID = -1;
 		(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
@@ -63,7 +64,7 @@ void DispellHelpfulMechanics::applyBattle(BattleInfo * battle, const BattleSpell
 	doDispell(battle, packet, Selector::positiveSpellEffects);	
 }
 
-ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance * caster,  const CStack * obj) const
+ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const ISpellCaster * caster,  const CStack * obj) const
 {
 	TBonusListPtr spellBon = obj->getSpellBonuses();
 	bool hasPositiveSpell = false;

+ 3 - 3
lib/spells/CreatureSpellMechanics.h

@@ -18,7 +18,7 @@ class DLL_LINKAGE AcidBreathDamageMechanics : public DefaultSpellMechanics
 public:
 	AcidBreathDamageMechanics(CSpell * s): DefaultSpellMechanics(s){};
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };
 
 class DLL_LINKAGE DeathStareMechanics : public DefaultSpellMechanics
@@ -26,7 +26,7 @@ class DLL_LINKAGE DeathStareMechanics : public DefaultSpellMechanics
 public:
 	DeathStareMechanics(CSpell * s): DefaultSpellMechanics(s){};
 protected:
-	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
+	void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
 };
 
 class DLL_LINKAGE DispellHelpfulMechanics : public DefaultSpellMechanics
@@ -36,5 +36,5 @@ public:
 
 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final;
 
-	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override;
 };

+ 59 - 0
lib/spells/ISpellMechanics.cpp

@@ -11,12 +11,71 @@
 #include "StdInc.h"
 #include "ISpellMechanics.h"
 
+#include "../BattleState.h"
+#include "../NetPacks.h"
+
 #include "CDefaultSpellMechanics.h"
 
 #include "AdventureSpellMechanics.h"
 #include "BattleSpellMechanics.h"
 #include "CreatureSpellMechanics.h"
 
+BattleSpellCastParameters::Destination::Destination(const CStack * destination):
+	stackValue(destination),
+	hexValue(destination->position)
+{
+	
+}
+
+BattleSpellCastParameters::Destination::Destination(const BattleHex & destination):
+	stackValue(nullptr),
+	hexValue(destination)	
+{
+	
+}
+
+BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell)
+	: cb(cb), caster(caster), casterColor(caster->getOwner()), casterSide(cb->whatSide(casterColor)),
+	casterHero(nullptr),
+	mode(ECastingMode::HERO_CASTING), casterStack(nullptr), selectedStack(nullptr),
+	spellLvl(-1),  effectLevel(-1), effectPower(0), enchantPower(0), effectValue(0)
+{
+	casterStack = dynamic_cast<const CStack *>(caster);
+	casterHero = dynamic_cast<const CGHeroInstance *>(caster);
+	prepare(spell);
+}
+
+void BattleSpellCastParameters::aimToHex(const BattleHex& destination)
+{
+	destinations.push_back(Destination(destination));
+}
+
+void BattleSpellCastParameters::aimToStack(const CStack * destination)
+{
+	destinations.push_back(Destination(destination));
+}
+
+
+BattleHex BattleSpellCastParameters::getFirstDestinationHex() const
+{
+	return destinations.at(0).hexValue;
+}
+
+void BattleSpellCastParameters::prepare(const CSpell * spell)
+{
+	spellLvl = caster->getSpellSchoolLevel(spell);
+	effectLevel = caster->getEffectLevel(spell);
+	effectPower = caster->getEffectPower(spell);
+	effectValue = caster->getEffectValue(spell);
+	enchantPower = caster->getEnchantPower(spell);
+	
+	vstd::amax(spellLvl, 0);
+	vstd::amax(effectLevel, 0);
+	vstd::amax(enchantPower, 0);	
+	vstd::amax(enchantPower, 0);
+	vstd::amax(effectValue, 0);
+}
+
 ///ISpellMechanics
 ISpellMechanics::ISpellMechanics(CSpell * s):
 	owner(s)

+ 71 - 4
lib/spells/ISpellMechanics.h

@@ -12,8 +12,75 @@
 
 #include "CSpellHandler.h"
 #include "../BattleHex.h"
-#include "../BattleState.h"
-#include "../NetPacks.h"
+
+
+///callback to be provided by server
+class DLL_LINKAGE SpellCastEnvironment
+{
+public:
+	virtual ~SpellCastEnvironment(){};
+	virtual void sendAndApply(CPackForClient * info) const = 0;
+
+	virtual CRandomGenerator & getRandomGenerator() const = 0;
+	virtual void complain(const std::string & problem) const = 0;
+
+	virtual const CMap * getMap() const = 0;
+	virtual const CGameInfoCallback * getCb() const = 0;
+
+	virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const =0;	//TODO: remove
+};
+
+///all parameters of particular cast event
+struct DLL_LINKAGE BattleSpellCastParameters
+{
+public:
+	///Single spell destination. 
+	/// (assumes that anything but battle stack can share same hex)
+	struct DLL_LINKAGE Destination
+	{
+		explicit Destination(const CStack * destination); 
+		explicit Destination(const BattleHex & destination);
+		
+		const CStack * stackValue;
+		const BattleHex hexValue;
+	};
+
+	BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell);
+	void aimToHex(const BattleHex & destination);
+	void aimToStack(const CStack * destination);
+	BattleHex getFirstDestinationHex() const;
+	
+	const BattleInfo * cb;
+	const ISpellCaster * caster;
+	const PlayerColor casterColor;	
+	const ui8 casterSide;
+
+	std::vector<Destination> destinations;
+
+	const CGHeroInstance * casterHero; //deprecated
+	ECastingMode::ECastingMode mode;
+	const CStack * casterStack; //deprecated
+	const CStack * selectedStack;//deprecated
+
+	///spell school level
+	int spellLvl;	
+	///spell school level to use for effects
+	int effectLevel;
+	///actual spell-power affecting effect values
+	int effectPower;
+	///actual spell-power affecting effect duration
+	int enchantPower;
+	///for Archangel-like casting
+	int effectValue;
+private:	
+	void prepare(const CSpell * spell);
+};
+
+struct DLL_LINKAGE AdventureSpellCastParameters
+{
+	const CGHeroInstance * caster;
+	int3 pos;
+};
 
 class DLL_LINKAGE ISpellMechanics
 {
@@ -39,9 +106,9 @@ public:
 	virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0;
 	virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0;
 	
-	virtual ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const = 0;
+	virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const = 0;
 	
-	virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
+	virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const = 0;
 	
 	virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0;
 	virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;

+ 18 - 2
lib/spells/Magic.h

@@ -17,6 +17,7 @@
 
 class CSpell;
 class CStack;
+class PlayerColor;
 
 class DLL_LINKAGE ISpellCaster
 {
@@ -26,7 +27,22 @@ public:
 	/// returns level on which given spell would be cast by this(0 - none, 1 - basic etc);
 	/// caster may not know this spell at all 
 	/// optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic
-	virtual ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const = 0;		
-	
+	virtual ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const = 0;	
+
+	///applying sorcery secondary skill etc
 	virtual ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const = 0;
+	
+	///default spell school level for effect calculation
+	virtual int getEffectLevel(const CSpell * spell) const = 0;
+
+	///default spell-power for damage/heal calculation
+	virtual int getEffectPower(const CSpell * spell) const = 0;
+
+	///default spell-power for timed effects duration
+	virtual int getEnchantPower(const CSpell * spell) const = 0;
+
+	///damage/heal override(ignores spell configuration, effect level and effect power)
+	virtual int getEffectValue(const CSpell * spell) const = 0;
+	
+	virtual const PlayerColor getOwner() const = 0;
 };

部分文件因为文件数量过多而无法显示