Explorar o código

Start spell-relatet files reorganisation
* moved existing files to separate directory
-> todo: split mechanics

AlexVinS %!s(int64=11) %!d(string=hai) anos
pai
achega
685deddac1
Modificáronse 59 ficheiros con 1513 adicións e 1508 borrados
  1. 1 1
      AI/BattleAI/BattleAI.cpp
  2. 1 1
      AI/VCAI/AIUtility.h
  3. 1 1
      AI/VCAI/VCAI.h
  4. 1 1
      CCallback.cpp
  5. 1 1
      client/CMT.cpp
  6. 1 1
      client/CMusicHandler.cpp
  7. 1 1
      client/CPlayerInterface.cpp
  8. 1 1
      client/CPreGame.cpp
  9. 1 1
      client/Client.cpp
  10. 1 1
      client/Graphics.cpp
  11. 1 1
      client/NetPacksClient.cpp
  12. 1 1
      client/battle/CBattleAnimations.cpp
  13. 1 1
      client/battle/CBattleInterface.cpp
  14. 1 1
      client/widgets/CArtifactHolder.cpp
  15. 1 1
      client/widgets/CComponent.cpp
  16. 1 1
      client/windows/CAdvmapInterface.cpp
  17. 1 1
      client/windows/CCastleInterface.cpp
  18. 1 1
      client/windows/CCreatureWindow.cpp
  19. 1 1
      client/windows/CSpellWindow.cpp
  20. 1 1
      client/windows/GUIClasses.cpp
  21. 1 1
      lib/BattleState.cpp
  22. 1 1
      lib/CArtHandler.cpp
  23. 1 1
      lib/CBattleCallback.cpp
  24. 1 1
      lib/CBonusTypeHandler.cpp
  25. 1 1
      lib/CCreatureSet.cpp
  26. 1 1
      lib/CGameInfoCallback.cpp
  27. 1 1
      lib/CGameState.cpp
  28. 5 3
      lib/CMakeLists.txt
  29. 1 1
      lib/CModHandler.cpp
  30. 1 1
      lib/CObstacleInstance.cpp
  31. 1 1
      lib/CTownHandler.cpp
  32. 1 1
      lib/GameConstants.cpp
  33. 1 1
      lib/HeroBonus.cpp
  34. 1 1
      lib/IGameCallback.cpp
  35. 1 1
      lib/NetPacksLib.cpp
  36. 1 1
      lib/VCMI_Lib.cpp
  37. 2 2
      lib/VCMI_lib.vcxproj
  38. 1 1
      lib/mapObjects/CBank.cpp
  39. 1 1
      lib/mapObjects/CGHeroInstance.cpp
  40. 1 1
      lib/mapObjects/CGPandoraBox.cpp
  41. 1 1
      lib/mapObjects/JsonRandom.cpp
  42. 1 1
      lib/mapObjects/MiscObjects.cpp
  43. 1 1
      lib/mapping/CMap.cpp
  44. 1 1
      lib/mapping/MapFormatH3M.cpp
  45. 1 1
      lib/registerTypes/RegisterTypes.cpp
  46. 1 1
      lib/registerTypes/TypesClientPacks1.cpp
  47. 1 1
      lib/registerTypes/TypesClientPacks2.cpp
  48. 1 1
      lib/registerTypes/TypesMapObjects1.cpp
  49. 1 1
      lib/registerTypes/TypesMapObjects2.cpp
  50. 1 1
      lib/registerTypes/TypesMapObjects3.cpp
  51. 1 1
      lib/registerTypes/TypesPregamePacks.cpp
  52. 1 1
      lib/registerTypes/TypesServerPacks.cpp
  53. 1 1
      lib/rmg/CRmgTemplateZone.cpp
  54. 1044 1041
      lib/spells/CSpellHandler.cpp
  55. 399 399
      lib/spells/CSpellHandler.h
  56. 9 9
      lib/spells/ISpellMechanics.cpp
  57. 1 1
      lib/spells/ISpellMechanics.h
  58. 1 1
      server/CGameHandler.cpp
  59. 1 1
      server/CVCMIServer.cpp

+ 1 - 1
AI/BattleAI/BattleAI.cpp

@@ -4,7 +4,7 @@
 #include "../../lib/BattleState.h"
 #include "../../CCallback.h"
 #include "../../lib/CCreatureHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/VCMI_Lib.h"
 
 using boost::optional;

+ 1 - 1
AI/VCAI/AIUtility.h

@@ -4,7 +4,7 @@
 #include "../../lib/CBuildingHandler.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/Connection.h"
 #include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"

+ 1 - 1
AI/VCAI/VCAI.h

@@ -12,7 +12,7 @@
 #include "../../lib/CBuildingHandler.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/Connection.h"
 #include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"

+ 1 - 1
CCallback.cpp

@@ -15,7 +15,7 @@
 #include "lib/Connection.h"
 #include "lib/NetPacks.h"
 #include "client/mapHandler.h"
-#include "lib/CSpellHandler.h"
+#include "lib/spells/CSpellHandler.h"
 #include "lib/CArtHandler.h"
 #include "lib/GameConstants.h"
 #ifdef min

+ 1 - 1
client/CMT.cpp

@@ -19,7 +19,7 @@
 #include "CVideoHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/CCreatureHandler.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "CMusicHandler.h"
 #include "CVideoHandler.h"
 #include "CDefHandler.h"

+ 1 - 1
client/CMusicHandler.cpp

@@ -4,7 +4,7 @@
 #include "CMusicHandler.h"
 #include "CGameInfo.h"
 #include "../lib/CCreatureHandler.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/JsonNode.h"
 #include "../lib/GameConstants.h"
 #include "../lib/filesystem/Filesystem.h"

+ 1 - 1
client/CPlayerInterface.cpp

@@ -23,7 +23,7 @@
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/Connection.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h" // For displaying correct UI when interacting with objects
 #include "../lib/BattleState.h"

+ 1 - 1
client/CPreGame.cpp

@@ -26,7 +26,7 @@
 #include "CPlayerInterface.h"
 #include "../CCallback.h"
 #include "CMessage.h"
-#include "../lib/CSpellHandler.h" /*for campaign bonuses*/
+#include "../lib/spells/CSpellHandler.h" /*for campaign bonuses*/
 #include "../lib/CArtHandler.h" /*for campaign bonuses*/
 #include "../lib/CBuildingHandler.h" /*for campaign bonuses*/
 #include "CBitmapHandler.h"

+ 1 - 1
client/Client.cpp

@@ -18,7 +18,7 @@
 #include "../lib/CHeroHandler.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/CBuildingHandler.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/Connection.h"
 #ifndef VCMI_ANDROID
 #include "../lib/Interprocess.h"

+ 1 - 1
client/Graphics.cpp

@@ -15,7 +15,7 @@
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CCreatureHandler.h"
 #include "CBitmapHandler.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/CGameState.h"
 #include "../lib/JsonNode.h"
 #include "../lib/vcmi_endian.h"

+ 1 - 1
client/NetPacksClient.cpp

@@ -13,7 +13,7 @@
 #include "../lib/VCMI_Lib.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/VCMIDirs.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/CSoundBase.h"
 #include "../lib/StartInfo.h"
 #include "mapHandler.h"

+ 1 - 1
client/battle/CBattleAnimations.cpp

@@ -20,7 +20,7 @@
 #include "../../lib/BattleState.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 
 /*
  * CBattleAnimations.cpp, part of VCMI engine

+ 1 - 1
client/battle/CBattleInterface.cpp

@@ -28,7 +28,7 @@
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/CondSh.h"
 #include "../../lib/CRandomGenerator.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"

+ 1 - 1
client/widgets/CArtifactHolder.cpp

@@ -16,7 +16,7 @@
 #include "../../CCallback.h"
 
 #include "../../lib/CArtHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
 
 #include "../../lib/mapObjects/CGHeroInstance.h"

+ 1 - 1
client/widgets/CComponent.cpp

@@ -12,7 +12,7 @@
 #include "../../lib/CArtHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/CCreatureHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/NetPacksBase.h"
 

+ 1 - 1
client/windows/CAdvmapInterface.cpp

@@ -32,7 +32,7 @@
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/CSoundBase.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/JsonNode.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"

+ 1 - 1
client/windows/CCastleInterface.cpp

@@ -26,7 +26,7 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CModHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"

+ 1 - 1
client/windows/CCreatureWindow.cpp

@@ -16,7 +16,7 @@
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CModHandler.h"
 #include "../../lib/CHeroHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CGameState.h"
 
 using namespace CSDL_Ext;

+ 1 - 1
client/windows/CSpellWindow.cpp

@@ -27,7 +27,7 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CHeroHandler.h"
-#include "../../lib/CSpellHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/CGameState.h"
 #include "../../lib/mapObjects/CGTownInstance.h"

+ 1 - 1
client/windows/GUIClasses.cpp

@@ -42,7 +42,7 @@
 #include "../lib/CHeroHandler.h"
 #include "../lib/CModHandler.h"
 #include "../lib/CondSh.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/CStopWatch.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/GameConstants.h"

+ 1 - 1
lib/BattleState.cpp

@@ -16,7 +16,7 @@
 #include "mapObjects/CObjectHandler.h"
 #include "CHeroHandler.h"
 #include "CCreatureHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "CTownHandler.h"
 #include "NetPacks.h"
 #include "JsonNode.h"

+ 1 - 1
lib/CArtHandler.cpp

@@ -15,7 +15,7 @@
 #include "CGeneralTextHandler.h"
 #include "VCMI_Lib.h"
 #include "CModHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "mapObjects/MapObjects.h"
 #include "NetPacksBase.h"
 #include "GameConstants.h"

+ 1 - 1
lib/CBattleCallback.cpp

@@ -3,7 +3,7 @@
 #include "BattleState.h"
 #include "CGameState.h"
 #include "NetPacks.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "VCMI_Lib.h"
 #include "CTownHandler.h"
 

+ 1 - 1
lib/CBonusTypeHandler.cpp

@@ -15,7 +15,7 @@
 
 #include "GameConstants.h"
 #include "CCreatureHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 
 ///MacroString
 

+ 1 - 1
lib/CCreatureSet.cpp

@@ -8,7 +8,7 @@
 #include "IGameCallback.h"
 #include "CGameState.h"
 #include "CGeneralTextHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "CHeroHandler.h"
 #include "IBonusTypeHandler.h"
 

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -17,7 +17,7 @@
 #include "BattleState.h" // for BattleInfo
 #include "NetPacks.h" // for InfoWindow
 #include "CModHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 
 //TODO make clean
 #define ERROR_VERBOSE_OR_NOT_RET_VAL_IF(cond, verbose, txt, retVal) do {if(cond){if(verbose)logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return retVal;}} while(0)

+ 1 - 1
lib/CGameState.cpp

@@ -7,7 +7,7 @@
 #include "CBuildingHandler.h"
 #include "CGeneralTextHandler.h"
 #include "CTownHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "CHeroHandler.h"
 #include "mapObjects/CObjectHandler.h"
 #include "CCreatureHandler.h"

+ 5 - 3
lib/CMakeLists.txt

@@ -54,6 +54,9 @@ set(lib_SRCS
 		rmg/CZoneGraphGenerator.cpp
 		rmg/CZonePlacer.cpp
 
+		spells/CSpellHandler.cpp
+		spells/ISpellMechanics.cpp
+
 		BattleAction.cpp
 		BattleHex.cpp
 		BattleState.cpp
@@ -71,7 +74,7 @@ set(lib_SRCS
 		CModHandler.cpp
 		CObstacleInstance.cpp
 		CRandomGenerator.cpp
-		CSpellHandler.cpp
+
 		CThreadHelper.cpp
 		CTownHandler.cpp
 		GameConstants.cpp
@@ -83,7 +86,6 @@ set(lib_SRCS
 		VCMI_Lib.cpp
 		VCMIDirs.cpp
 		IHandlerBase.cpp
-                SpellMechanics.cpp
 
 		IGameCallback.cpp
 		CGameInfoCallback.cpp
@@ -96,7 +98,7 @@ set(lib_SRCS
 		registerTypes/TypesClientPacks2.cpp
 		registerTypes/TypesMapObjects1.cpp
 		registerTypes/TypesMapObjects2.cpp
-				registerTypes/TypesMapObjects3.cpp
+		registerTypes/TypesMapObjects3.cpp
 		registerTypes/TypesPregamePacks.cpp
 		registerTypes/TypesServerPacks.cpp
 )

+ 1 - 1
lib/CModHandler.cpp

@@ -14,7 +14,7 @@
 #include "StringConstants.h"
 #include "CStopWatch.h"
 #include "IHandlerBase.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 
 /*
  * CModHandler.cpp, part of VCMI engine

+ 1 - 1
lib/CObstacleInstance.cpp

@@ -2,7 +2,7 @@
 #include "CObstacleInstance.h"
 #include "CHeroHandler.h"
 #include "VCMI_Lib.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 
 /*
  * CObstacleInstance.cpp, part of VCMI engine

+ 1 - 1
lib/CTownHandler.cpp

@@ -9,7 +9,7 @@
 #include "CModHandler.h"
 #include "CHeroHandler.h"
 #include "CArtHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "filesystem/Filesystem.h"
 #include "mapObjects/CObjectClassesHandler.h"
 #include "mapObjects/CObjectHandler.h"

+ 1 - 1
lib/GameConstants.cpp

@@ -16,7 +16,7 @@
 #include "mapObjects/CObjectClassesHandler.h"
 #include "CArtHandler.h"
 #include "CCreatureHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 
 const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
 const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253);

+ 1 - 1
lib/HeroBonus.cpp

@@ -12,7 +12,7 @@
 #include "HeroBonus.h"
 
 #include "VCMI_Lib.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "CCreatureHandler.h"
 #include "CCreatureSet.h"
 #include "CHeroHandler.h"

+ 1 - 1
lib/IGameCallback.cpp

@@ -12,7 +12,7 @@
 #include "IGameCallback.h"
 
 #include "CHeroHandler.h" // for CHeroHandler
-#include "CSpellHandler.h" // for CSpell
+#include "spells/CSpellHandler.h"// for CSpell
 #include "NetPacks.h"
 #include "CBonusTypeHandler.h"
 #include "CModHandler.h"

+ 1 - 1
lib/NetPacksLib.cpp

@@ -9,7 +9,7 @@
 #include "CModHandler.h"
 #include "VCMI_Lib.h"
 #include "mapping/CMap.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "CCreatureHandler.h"
 #include "CGameState.h"
 #include "BattleState.h"

+ 1 - 1
lib/VCMI_Lib.cpp

@@ -19,7 +19,7 @@
 #include "mapObjects/CObjectHandler.h"
 #include "CTownHandler.h"
 #include "CBuildingHandler.h"
-#include "CSpellHandler.h"
+#include "spells/CSpellHandler.h"
 #include "CGeneralTextHandler.h"
 #include "CModHandler.h"
 #include "IGameEventsReceiver.h"

+ 2 - 2
lib/VCMI_lib.vcxproj

@@ -184,11 +184,11 @@
     <ClCompile Include="CModHandler.cpp" />
     <ClCompile Include="CObstacleInstance.cpp" />
     <ClCompile Include="Connection.cpp" />
-    <ClCompile Include="CSpellHandler.cpp" />
-    <ClCompile Include="SpellMechanics.cpp" />
     <ClCompile Include="CThreadHelper.cpp" />
     <ClCompile Include="CTownHandler.cpp" />
     <ClCompile Include="CRandomGenerator.cpp" />
+    <ClCompile Include="spells\CSpellHandler.cpp" />
+    <ClCompile Include="spells\ISpellMechanics.cpp" />
     <ClCompile Include="filesystem\AdapterLoaders.cpp" />
     <ClCompile Include="filesystem\CArchiveLoader.cpp" />
     <ClCompile Include="filesystem\CBinaryReader.cpp" />

+ 1 - 1
lib/mapObjects/CBank.cpp

@@ -15,7 +15,7 @@
 #include "../CGeneralTextHandler.h"
 #include "../CSoundBase.h"
 #include "CommonConstructors.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
 

+ 1 - 1
lib/mapObjects/CGHeroInstance.cpp

@@ -16,7 +16,7 @@
 #include "../CHeroHandler.h"
 #include "../CModHandler.h"
 #include "../CSoundBase.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "CObjectClassesHandler.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"

+ 1 - 1
lib/mapObjects/CGPandoraBox.cpp

@@ -14,7 +14,7 @@
 #include "../NetPacks.h"
 #include "../CSoundBase.h"
 
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../StartInfo.h"
 #include "../IGameCallback.h"
 

+ 1 - 1
lib/mapObjects/JsonRandom.cpp

@@ -20,7 +20,7 @@
 #include "../CArtHandler.h"
 #include "../CCreatureHandler.h"
 #include "../CCreatureSet.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 
 namespace JsonRandom
 {

+ 1 - 1
lib/mapObjects/MiscObjects.cpp

@@ -17,7 +17,7 @@
 #include "../CModHandler.h"
 
 #include "CObjectClassesHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
 

+ 1 - 1
lib/mapping/CMap.cpp

@@ -9,7 +9,7 @@
 #include "../mapObjects/CObjectClassesHandler.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../CGeneralTextHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "CMapEditManager.h"
 
 SHeroName::SHeroName() : heroId(-1)

+ 1 - 1
lib/mapping/MapFormatH3M.cpp

@@ -16,7 +16,7 @@
 
 #include "../CStopWatch.h"
 #include "../filesystem/Filesystem.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CCreatureHandler.h"
 #include "../CGeneralTextHandler.h"
 #include "../CHeroHandler.h"

+ 1 - 1
lib/registerTypes/RegisterTypes.cpp

@@ -13,7 +13,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/registerTypes/TypesClientPacks1.cpp

@@ -12,7 +12,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/registerTypes/TypesClientPacks2.cpp

@@ -12,7 +12,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/registerTypes/TypesMapObjects1.cpp

@@ -12,7 +12,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/registerTypes/TypesMapObjects2.cpp

@@ -12,7 +12,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/registerTypes/TypesMapObjects3.cpp

@@ -12,7 +12,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/registerTypes/TypesPregamePacks.cpp

@@ -12,7 +12,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/registerTypes/TypesServerPacks.cpp

@@ -12,7 +12,7 @@
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
 #include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
+#include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"

+ 1 - 1
lib/rmg/CRmgTemplateZone.cpp

@@ -17,7 +17,7 @@
 #include "../VCMI_Lib.h"
 #include "../CTownHandler.h"
 #include "../CCreatureHandler.h"
-#include "../CSpellHandler.h" //for choosing random spells
+#include "../spells/CSpellHandler.h" //for choosing random spells
 
 #include "../mapObjects/CommonConstructors.h"
 #include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h

+ 1044 - 1041
lib/CSpellHandler.cpp → lib/spells/CSpellHandler.cpp

@@ -1,1041 +1,1044 @@
-#include "StdInc.h"
-#include "CSpellHandler.h"
-
-#include "CGeneralTextHandler.h"
-#include "filesystem/Filesystem.h"
-
-#include "JsonNode.h"
-#include <cctype>
-#include "BattleHex.h"
-#include "CModHandler.h"
-#include "StringConstants.h"
-
-#include "mapObjects/CGHeroInstance.h"
-#include "BattleState.h"
-#include "CBattleCallback.h"
-
-#include "SpellMechanics.h"
-
-
-
-/*
- * CSpellHandler.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-namespace SpellConfig
-{
-	static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
-	
-	static const SpellSchoolInfo SCHOOL[4] = 
-	{
-		{
-			ESpellSchool::AIR,
-			Bonus::AIR_SPELL_DMG_PREMY,
-			Bonus::AIR_IMMUNITY,
-			"air",
-			SecondarySkill::AIR_MAGIC,
-			Bonus::AIR_SPELLS
-		},
-		{
-			ESpellSchool::FIRE,
-			Bonus::FIRE_SPELL_DMG_PREMY,
-			Bonus::FIRE_IMMUNITY,
-			"fire",
-			SecondarySkill::FIRE_MAGIC,
-			Bonus::FIRE_SPELLS
-		},
-		{
-			ESpellSchool::WATER,
-			Bonus::WATER_SPELL_DMG_PREMY,
-			Bonus::WATER_IMMUNITY,
-			"water",
-			SecondarySkill::WATER_MAGIC,
-			Bonus::WATER_SPELLS
-		},
-		{
-			ESpellSchool::EARTH,
-			Bonus::EARTH_SPELL_DMG_PREMY,
-			Bonus::EARTH_IMMUNITY,
-			"earth",
-			SecondarySkill::EARTH_MAGIC,
-			Bonus::EARTH_SPELLS
-		}
-	};	
-}
-
-BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo* cb)
-	: spellLvl(0), destination(BattleHex::INVALID), casterSide(0),casterColor(PlayerColor::CANNOT_DETERMINE),caster(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")
-{
-
-}
-
-CSpell::LevelInfo::~LevelInfo()
-{
-
-}
-
-///CSpell
-CSpell::CSpell():
-	id(SpellID::NONE), level(0),
-	combatSpell(false), creatureAbility(false),
-	positiveness(ESpellPositiveness::NEUTRAL),
-	defaultProbability(0),
-	isRising(false), isDamage(false), isOffensive(false),
-	targetType(ETargetType::NO_TARGET),
-	mechanics(nullptr)
-{
-	levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
-}
-
-CSpell::~CSpell()
-{
-	delete mechanics;
-}
-
-void CSpell::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
-{
-	mechanics->applyBattle(battle, packet);
-}
-
-bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
-{
-	assert(env);
-		
-	return mechanics->adventureCast(env, parameters);
-}
-
-void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
-{
-	assert(env);
-	
-	mechanics->battleCast(env, parameters);
-}
-
-bool CSpell::isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const
-{
-	if(!hasSpellBook)
-		return false;
-	
-	const bool inSpellBook = vstd::contains(spellBook, id);
-	const bool isBonus = caster->hasBonusOfType(Bonus::SPELL, id);
-	
-	bool inTome = false;
-	
-	forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
-	{
-		if(caster->hasBonusOfType(cnf.knoledgeBonus))
-		{
-			inTome = stop = true;
-		}				
-	});	
-
-    if (isSpecialSpell())
-    {
-        if (inSpellBook)
-        {//hero has this spell in spellbook
-            logGlobal->errorStream() << "Special spell in spellbook "<<name;
-        }
-        return isBonus;
-    }
-    else
-    {
-       return inSpellBook || inTome || isBonus || caster->hasBonusOfType(Bonus::SPELLS_OF_LEVEL, level);
-    }	
-}
-
-const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
-{
-	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
-	{
-		logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
-		throw new std::runtime_error("Invalid school level");
-	}
-
-	return levels.at(level);
-}
-
-ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance* caster, const CStack* affectedCreature) const
-{
-	ui32 ret = baseDamage;
-	//applying sorcery secondary skill
-	if(caster)
-	{
-		ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
-		ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id.toEnum())) / 100.0;
-		
-		forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
-		{
-			ret *= (100.0 + caster->valOfBonuses(cnf.damagePremyBonus)) / 100.0;
-			stop = true; //only bonus from one school is used
-		});		
-
-		if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
-			ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, id.toEnum()) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
-	}
-	return ret;	
-}
-
-ui32 CSpell::calculateDamage(const CGHeroInstance * 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;
-		}
-	}
-	ret = calculateBonus(ret, caster, affectedCreature);
-	return ret;	
-}
-
-std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
-{
-	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
-{
-	ISpellMechanics::SpellTargetingContext ctx(this, cb,mode,casterColor,spellLvl,destination);
-
-	std::set<const CStack* > attackedCres = mechanics->getAffectedStacks(ctx);
-	
-	//now handle immunities		
-	auto predicate = [&, this](const CStack * s)->bool
-	{
-		bool hitDirectly = ctx.ti.alwaysHitDirectly && s->coversPos(destination);
-		bool notImmune = (ESpellCastProblem::OK == isImmuneByStack(caster, s));
-		
-		return !(hitDirectly || notImmune);  
-	};	
-	vstd::erase_if(attackedCres, predicate);
-	
-	return attackedCres;
-}
-
-
-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;
-	for(const SpellSchoolInfo & cnf : SpellConfig::SCHOOL)
-	{
-		if(school.at(cnf.id))
-		{
-			cb(cnf, stop);
-			
-			if(stop)
-				break;
-		}				
-	}	
-}
-
-
-bool CSpell::isCombatSpell() const
-{
-	return combatSpell;
-}
-
-bool CSpell::isAdventureSpell() const
-{
-	return !combatSpell;
-}
-
-bool CSpell::isCreatureAbility() const
-{
-	return creatureAbility;
-}
-
-bool CSpell::isPositive() const
-{
-	return positiveness == POSITIVE;
-}
-
-bool CSpell::isNegative() const
-{
-	return positiveness == NEGATIVE;
-}
-
-bool CSpell::isNeutral() const
-{
-	return positiveness == NEUTRAL;
-}
-
-bool CSpell::isHealingSpell() const
-{
-	return isRisingSpell() || (id == SpellID::CURE);
-}
-
-bool CSpell::isRisingSpell() const
-{
-	return isRising;
-}
-
-bool CSpell::isDamageSpell() const
-{
-	return isDamage;
-}
-
-bool CSpell::isOffensiveSpell() const
-{
-	return isOffensive;
-}
-
-bool CSpell::isSpecialSpell() const
-{
-	return isSpecial;
-}
-
-bool CSpell::hasEffects() const
-{
-	return !levels[0].effects.empty();
-}
-
-const std::string& CSpell::getIconImmune() const
-{
-	return iconImmune;
-}
-
-const std::string& CSpell::getCastSound() const
-{
-	return castSound;
-}
-
-
-
-si32 CSpell::getCost(const int skillLevel) const
-{
-	return getLevelInfo(skillLevel).cost;
-}
-
-si32 CSpell::getPower(const int skillLevel) const
-{
-	return getLevelInfo(skillLevel).power;
-}
-
-si32 CSpell::getProbability(const TFaction factionId) const
-{
-	if(!vstd::contains(probabilities,factionId))
-	{
-		return defaultProbability;
-	}
-	return probabilities.at(factionId);
-}
-
-
-void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
-{
-	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
-	{
-		logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
-		return;
-	}
-
-	const std::vector<Bonus> & effects = levels[level].effects;
-
-	if(effects.empty())
-	{
-		logGlobal->errorStream() << __FUNCTION__ << " This spell ("  + name + ") has no effects for level " << level;
-		return;
-	}
-
-	lst.reserve(lst.size() + effects.size());
-
-	for(const Bonus & b : effects)
-	{
-		lst.push_back(Bonus(b));
-	}
-}
-
-ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * 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){
-		return s->coversPos(destination) && (isRisingSpell() || s->alive());
-	});
-	
-	if(!stacks.empty())
-	{
-		bool allImmune = true;
-		
-		ESpellCastProblem::ESpellCastProblem problem;		
-		
-		for(auto s : stacks)
-		{
-			ESpellCastProblem::ESpellCastProblem res = isImmuneByStack(caster,s);
-			
-			if(res == ESpellCastProblem::OK)
-			{
-				allImmune = false;
-			}
-			else
-			{
-				problem = res;
-			}
-		}
-		
-		if(allImmune)
-			return problem;
-	}
-	else //no target stack on this tile
-	{
-		if(getTargetType() == CSpell::CREATURE)
-		{
-			if(caster && mode == ECastingMode::HERO_CASTING) //TODO why???
-			{
-				const CSpell::TargetInfo ti(this, caster->getSpellSchoolLevel(this), mode);
-				
-				if(!ti.massive)
-					return ESpellCastProblem::WRONG_SPELL_TARGET;					
-			}
-			else
-			{
- 				return ESpellCastProblem::WRONG_SPELL_TARGET;
-			}			
-		}
-	}
-
-	return ESpellCastProblem::OK;	
-}
-
-
-ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) const
-{	
-	//todo: use new bonus API
-	//1. Check absolute limiters
-	for(auto b : absoluteLimiters)
-	{
-		if (!obj->hasBonusOfType(b))
-			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-	}
-
-	//2. Check absolute immunities
-	for(auto b : absoluteImmunities)
-	{
-		if (obj->hasBonusOfType(b))
-			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
-		return ESpellCastProblem::NOT_DECIDED;
-		
-	//4. Check negatable limit
-	for(auto b : limiters)
-	{
-		if (!obj->hasBonusOfType(b))
-			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-	}
-
-
-	//5. Check negatable immunities
-	for(auto b : immunities)
-	{
-		if (obj->hasBonusOfType(b))
-			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-	}
-
-	//6. Check elemental immunities
-	
-	ESpellCastProblem::ESpellCastProblem tmp = ESpellCastProblem::NOT_DECIDED;
-	
-	forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
-	{
-		auto element = cnf.immunityBonus;
-		
-		if(obj->hasBonusOfType(element, 0)) //always resist if immune to all spells altogether
-		{
-			tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-			stop = true;
-		}				
-		else if(!isPositive()) //negative or indifferent
-		{
-			if((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
-			{
-				tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-				stop = true;
-			}			
-		}	
-	});
-	
-	if(tmp != ESpellCastProblem::NOT_DECIDED)
-		return tmp;
-	
-	TBonusListPtr levelImmunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
-
-	if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id)
-		|| ( levelImmunities->size() > 0  &&  levelImmunities->totalValue() >= level  &&  level))
-	{
-		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-	}
-
-	return ESpellCastProblem::NOT_DECIDED;
-}
-
-ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const
-{
-	const auto immuneResult = mechanics->isImmuneByStack(caster,obj);
-	
-	if (ESpellCastProblem::NOT_DECIDED != immuneResult) 
-		return immuneResult;
-	return ESpellCastProblem::OK;	
-}
-
-
-void CSpell::setIsOffensive(const bool val)
-{
-	isOffensive = val;
-
-	if(val)
-	{
-		positiveness = CSpell::NEGATIVE;
-		isDamage = true;
-	}
-}
-
-void CSpell::setIsRising(const bool val)
-{
-	isRising = val;
-
-	if(val)
-	{
-		positiveness = CSpell::POSITIVE;
-	}
-}
-
-void CSpell::setup()
-{
-	setupMechanics();
-}
-
-
-void CSpell::setupMechanics()
-{
-	if(nullptr != mechanics)
-	{
-		logGlobal->errorStream() << "Spell " << this->name << " mechanics already set";
-		delete mechanics;
-	}
-	
-	mechanics = ISpellMechanics::createMechanics(this);	
-}
-
-///CSpell::AnimationInfo
-CSpell::AnimationInfo::AnimationInfo()
-{
-	
-}
-
-CSpell::AnimationInfo::~AnimationInfo()
-{
-	
-}
-
-std::string CSpell::AnimationInfo::selectProjectile(const double angle) const
-{	
-	std::string res;	
-	double maximum = 0.0;
-	
-	for(const auto & info : projectile)
-	{
-		if(info.minimumAngle < angle && info.minimumAngle > maximum)
-		{
-			maximum = info.minimumAngle;
-			res = info.resourceName;
-		}
-	}
-	
-	return std::move(res);	
-}
-
-
-///CSpell::TargetInfo
-CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level)
-{
-	init(spell, level);
-}
-
-CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level, ECastingMode::ECastingMode mode)
-{
-	init(spell, level);
-	if(mode == ECastingMode::ENCHANTER_CASTING)
-	{
-		smart = true; //FIXME: not sure about that, this makes all spells smart in this mode
-		massive = true;
-	}
-	else if(mode == ECastingMode::SPELL_LIKE_ATTACK)
-	{
-		alwaysHitDirectly = true;
-	}	
-}
-
-void CSpell::TargetInfo::init(const CSpell * spell, const int level)
-{
-	auto & levelInfo = spell->getLevelInfo(level);
-
-	type = spell->getTargetType();
-	smart = levelInfo.smartTarget;
-	massive = levelInfo.range == "X";
-	onlyAlive = !spell->isRisingSpell();
-	alwaysHitDirectly = false;
-	clearAffected = levelInfo.clearAffected;
-	clearTarget = levelInfo.clearTarget;
-}
-
-
-bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
-{
-	int3 diff = pos - center;
-	if(diff.x >= -9  &&  diff.x <= 9  &&  diff.y >= -8  &&  diff.y <= 8)
-		return true;
-	else
-		return false;
-}
-
-///CSpellHandler
-CSpellHandler::CSpellHandler()
-{
-
-}
-
-std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
-{
-	using namespace SpellConfig;
-	std::vector<JsonNode> legacyData;
-
-	CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
-
-	auto readSchool = [&](JsonMap& schools, const std::string& name)
-	{
-		if (parser.readString() == "x")
-		{
-			schools[name].Bool() = true;
-		}
-	};
-
-	auto read = [&,this](bool combat, bool ability)
-	{
-		do
-		{
-			JsonNode lineNode(JsonNode::DATA_STRUCT);
-
-			const si32 id = legacyData.size();
-
-			lineNode["index"].Float() = id;
-			lineNode["type"].String() = ability ? "ability" : (combat ? "combat" : "adventure");
-
-			lineNode["name"].String() = parser.readString();
-
-			parser.readString(); //ignored unused abbreviated name
-			lineNode["level"].Float()      = parser.readNumber();
-
-			auto& schools = lineNode["school"].Struct();
-
-			readSchool(schools, "earth");
-			readSchool(schools, "water");
-			readSchool(schools, "fire");
-			readSchool(schools, "air");
-
-			auto& levels = lineNode["levels"].Struct();
-
-			auto getLevel = [&](const size_t idx)->JsonMap&
-			{
-				assert(idx < GameConstants::SPELL_SCHOOL_LEVELS);
-				return levels[LEVEL_NAMES[idx]].Struct();
-			};
-
-			auto costs = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
-			lineNode["power"].Float() = parser.readNumber();
-			auto powers = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
-
-			auto& chances = lineNode["gainChance"].Struct();
-
-			for(size_t i = 0; i < GameConstants::F_NUMBER ; i++){
-				chances[ETownType::names[i]].Float() = parser.readNumber();
-			}
-
-			auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
-
-			std::vector<std::string> descriptions;
-			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS ; i++)
-				descriptions.push_back(parser.readString());
-
-			parser.readString(); //ignore attributes. All data present in JSON
-
-			//save parsed level specific data
-			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
-			{
-				auto& level = getLevel(i);
-				level["description"].String() = descriptions[i];
-				level["cost"].Float() = costs[i];
-				level["power"].Float() = powers[i];
-				level["aiValue"].Float() = AIVals[i];
-			}
-
-			legacyData.push_back(lineNode);
-
-
-		}
-		while (parser.endLine() && !parser.isNextEntryEmpty());
-	};
-
-	auto skip = [&](int cnt)
-	{
-		for(int i=0; i<cnt; i++)
-			parser.endLine();
-	};
-
-	skip(5);// header
-	read(false,false); //read adventure map spells
-	skip(3);
-	read(true,false); //read battle spells
-	skip(3);
-	read(true,true);//read creature abilities
-
-    //TODO: maybe move to config
-	//clone Acid Breath attributes for Acid Breath damage effect
-	JsonNode temp = legacyData[SpellID::ACID_BREATH_DEFENSE];
-	temp["index"].Float() = SpellID::ACID_BREATH_DAMAGE;
-	legacyData.push_back(temp);
-
-	objects.resize(legacyData.size());
-
-	return legacyData;
-}
-
-const std::string CSpellHandler::getTypeName() const
-{
-	return "spell";
-}
-
-CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
-{
-	using namespace SpellConfig;
-
-	CSpell * spell = new CSpell();
-
-	const auto type = json["type"].String();
-
-	if(type == "ability")
-	{
-		spell->creatureAbility = true;
-		spell->combatSpell = true;
-	}
-	else
-	{
-		spell->creatureAbility = false;
-		spell->combatSpell = type == "combat";
-	}
-
-	spell->name = json["name"].String();
-
-	logGlobal->traceStream() << __FUNCTION__ << ": loading spell " << spell->name;
-
-	const auto schoolNames = json["school"];
-	
-	for(const SpellSchoolInfo & info : SpellConfig::SCHOOL)
-	{
-		spell->school[info.id] = schoolNames[info.jsonName].Bool();
-	}
-
-	spell->level = json["level"].Float();
-	spell->power = json["power"].Float();
-
-	spell->defaultProbability = json["defaultGainChance"].Float();
-
-	for(const auto & node : json["gainChance"].Struct())
-	{
-		const int chance = node.second.Float();
-
-		VLC->modh->identifiers.requestIdentifier(node.second.meta, "faction",node.first, [=](si32 factionID)
-		{
-			spell->probabilities[factionID] = chance;
-		});
-	}
-
-	auto targetType = json["targetType"].String();
-
-	if(targetType == "NO_TARGET")
-		spell->targetType = CSpell::NO_TARGET;
-	else if(targetType == "CREATURE")
-		spell->targetType = CSpell::CREATURE;
-	else if(targetType == "OBSTACLE")
-		spell->targetType = CSpell::OBSTACLE;
-	else if(targetType == "LOCATION")
-		spell->targetType = CSpell::LOCATION;
-	else 
-		logGlobal->warnStream() << "Spell " << spell->name << ". Target type " << (targetType.empty() ? "empty" : "unknown ("+targetType+")") << ". Assumed NO_TARGET.";
-
-	for(const auto & counteredSpell: json["counters"].Struct())
-		if (counteredSpell.second.Bool())
-		{
-			VLC->modh->identifiers.requestIdentifier(json.meta, counteredSpell.first, [=](si32 id)
-			{
-				spell->counteredSpells.push_back(SpellID(id));
-			});
-		}
-
-	//TODO: more error checking - f.e. conflicting flags
-	const auto flags = json["flags"];
-
-	//by default all flags are set to false in constructor
-
-	spell->isDamage = flags["damage"].Bool(); //do this before "offensive"
-
-	if(flags["offensive"].Bool())
-	{
-		spell->setIsOffensive(true);
-	}
-
-	if(flags["rising"].Bool())
-	{
-		spell->setIsRising(true);
-	}
-
-	const bool implicitPositiveness = spell->isOffensive || spell->isRising; //(!) "damage" does not mean NEGATIVE  --AVS
-
-	if(flags["indifferent"].Bool())
-	{
-		spell->positiveness = CSpell::NEUTRAL;
-	}
-	else if(flags["negative"].Bool())
-	{
-		spell->positiveness = CSpell::NEGATIVE;
-	}
-	else if(flags["positive"].Bool())
-	{
-		spell->positiveness = CSpell::POSITIVE;
-	}
-	else if(!implicitPositiveness)
-	{
-		spell->positiveness = CSpell::NEUTRAL; //duplicates constructor but, just in case
-		logGlobal->errorStream() << "No positiveness specified, assumed NEUTRAL";
-	}
-
-	spell->isSpecial = flags["special"].Bool();
-
-	auto findBonus = [&](std::string name, std::vector<Bonus::BonusType> &vec)
-	{
-		auto it = bonusNameMap.find(name);
-		if(it == bonusNameMap.end())
-		{
-			logGlobal->errorStream() << spell->name << ": invalid bonus name" << name;
-		}
-		else
-		{
-			vec.push_back((Bonus::BonusType)it->second);
-		}
-	};
-
-	auto readBonusStruct = [&](std::string name, std::vector<Bonus::BonusType> &vec)
-	{
-		for(auto bonusData: json[name].Struct())
-		{
-			const std::string bonusId = bonusData.first;
-			const bool flag = bonusData.second.Bool();
-
-			if(flag)
-				findBonus(bonusId, vec);
-		}
-	};
-
-	readBonusStruct("immunity", spell->immunities);
-	readBonusStruct("absoluteImmunity", spell->absoluteImmunities);
-	readBonusStruct("limit", spell->limiters);	
-	readBonusStruct("absoluteLimit", spell->absoluteLimiters);
-
-
-	const JsonNode & graphicsNode = json["graphics"];
-
-	spell->iconImmune = graphicsNode["iconImmune"].String();
-	spell->iconBook = graphicsNode["iconBook"].String();
-	spell->iconEffect = graphicsNode["iconEffect"].String();
-	spell->iconScenarioBonus = graphicsNode["iconScenarioBonus"].String();
-	spell->iconScroll = graphicsNode["iconScroll"].String();
-
-	const JsonNode & animationNode = json["animation"];
-	
-	auto loadAnimationQueue = [&](const std::string & jsonName, CSpell::TAnimationQueue & q)	
-	{
-		auto queueNode = animationNode[jsonName].Vector();		
-		for(const JsonNode & item : queueNode)
-		{	
-			CSpell::TAnimation newItem;
-			newItem.verticalPosition = VerticalPosition::TOP;
-			
-			if(item.getType() == JsonNode::DATA_STRING)
-				newItem.resourceName = item.String();
-			else if(item.getType() == JsonNode::DATA_STRUCT)
-			{
-				newItem.resourceName = item["defName"].String();
-				
-				auto vPosStr = item["verticalPosition"].String();
-				if("bottom" == vPosStr)
-					newItem.verticalPosition = VerticalPosition::BOTTOM;
-			}	
-			q.push_back(newItem);		
-		}
-	};
-	
-	loadAnimationQueue("affect", spell->animationInfo.affect);
-	loadAnimationQueue("cast", spell->animationInfo.cast);
-	loadAnimationQueue("hit", spell->animationInfo.hit);	
-	
-	const JsonVector & projectile = animationNode["projectile"].Vector();
-	
-	for(const JsonNode & item : projectile)
-	{
-		CSpell::ProjectileInfo info;
-		info.resourceName = item["defName"].String();
-		info.minimumAngle = item["minimumAngle"].Float();
-		
-		spell->animationInfo.projectile.push_back(info);
-	}
-
-	const JsonNode & soundsNode = json["sounds"];
-	spell->castSound = soundsNode["cast"].String();
-
-
-	//load level attributes
-
-	const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS;
-
-	for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++)
-	{
-		const JsonNode & levelNode = json["levels"][LEVEL_NAMES[levelIndex]];
-		
-		CSpell::LevelInfo & levelObject = spell->levels[levelIndex];
-
-		const si32 levelPower     = levelObject.power = levelNode["power"].Float();		
-
-		levelObject.description   = levelNode["description"].String();
-		levelObject.cost          = levelNode["cost"].Float();
-		levelObject.AIValue       = levelNode["aiValue"].Float();
-		levelObject.smartTarget   = levelNode["targetModifier"]["smart"].Bool();
-		levelObject.clearTarget   = levelNode["targetModifier"]["clearTarget"].Bool();
-		levelObject.clearAffected = levelNode["targetModifier"]["clearAffected"].Bool();			
-		levelObject.range         = levelNode["range"].String();
-		
-		for(const auto & elem : levelNode["effects"].Struct())
-		{
-			const JsonNode & bonusNode = elem.second;
-			Bonus * b = JsonUtils::parseBonus(bonusNode);
-			const bool usePowerAsValue = bonusNode["val"].isNull();
-
-			//TODO: make this work. see CSpellHandler::afterLoadFinalization()
-			//b->sid = spell->id; //for all
-
-			b->source = Bonus::SPELL_EFFECT;//for all
-
-			if(usePowerAsValue)
-				b->val = levelPower;
-
-			levelObject.effects.push_back(*b);
-		}
-		
-	}
-
-	return spell;
-}
-
-void CSpellHandler::afterLoadFinalization()
-{
-	//FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
-	for(auto spell: objects)
-	{
-		for(auto & level: spell->levels)
-			for(auto & bonus: level.effects)
-				bonus.sid = spell->id;
-		spell->setup();
-	}
-}
-
-void CSpellHandler::beforeValidate(JsonNode & object)
-{
-	//handle "base" level info
-	
-	JsonNode& levels = object["levels"];
-	JsonNode& base = levels["base"];
-	
-	auto inheritNode = [&](const std::string & name){
-		JsonUtils::inherit(levels[name],base);
-	};
-	
-	inheritNode("none");
-	inheritNode("basic");
-	inheritNode("advanced");
-	inheritNode("expert");
-
-}
-
-
-CSpellHandler::~CSpellHandler()
-{
-
-}
-
-std::vector<bool> CSpellHandler::getDefaultAllowed() const
-{
-	std::vector<bool> allowedSpells;
-	allowedSpells.resize(GameConstants::SPELLS_QUANTITY, true);
-	return allowedSpells;
-}
+#include "StdInc.h"
+
+#include <cctype>
+
+#include "CSpellHandler.h"
+
+#include "../CGeneralTextHandler.h"
+#include "../filesystem/Filesystem.h"
+
+#include "../JsonNode.h"
+
+#include "../BattleHex.h"
+#include "../CModHandler.h"
+#include "../StringConstants.h"
+
+#include "../mapObjects/CGHeroInstance.h"
+#include "../BattleState.h"
+#include "../CBattleCallback.h"
+
+#include "ISpellMechanics.h"
+
+
+
+/*
+ * CSpellHandler.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+namespace SpellConfig
+{
+	static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
+	
+	static const SpellSchoolInfo SCHOOL[4] = 
+	{
+		{
+			ESpellSchool::AIR,
+			Bonus::AIR_SPELL_DMG_PREMY,
+			Bonus::AIR_IMMUNITY,
+			"air",
+			SecondarySkill::AIR_MAGIC,
+			Bonus::AIR_SPELLS
+		},
+		{
+			ESpellSchool::FIRE,
+			Bonus::FIRE_SPELL_DMG_PREMY,
+			Bonus::FIRE_IMMUNITY,
+			"fire",
+			SecondarySkill::FIRE_MAGIC,
+			Bonus::FIRE_SPELLS
+		},
+		{
+			ESpellSchool::WATER,
+			Bonus::WATER_SPELL_DMG_PREMY,
+			Bonus::WATER_IMMUNITY,
+			"water",
+			SecondarySkill::WATER_MAGIC,
+			Bonus::WATER_SPELLS
+		},
+		{
+			ESpellSchool::EARTH,
+			Bonus::EARTH_SPELL_DMG_PREMY,
+			Bonus::EARTH_IMMUNITY,
+			"earth",
+			SecondarySkill::EARTH_MAGIC,
+			Bonus::EARTH_SPELLS
+		}
+	};	
+}
+
+BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo* cb)
+	: spellLvl(0), destination(BattleHex::INVALID), casterSide(0),casterColor(PlayerColor::CANNOT_DETERMINE),caster(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")
+{
+
+}
+
+CSpell::LevelInfo::~LevelInfo()
+{
+
+}
+
+///CSpell
+CSpell::CSpell():
+	id(SpellID::NONE), level(0),
+	combatSpell(false), creatureAbility(false),
+	positiveness(ESpellPositiveness::NEUTRAL),
+	defaultProbability(0),
+	isRising(false), isDamage(false), isOffensive(false),
+	targetType(ETargetType::NO_TARGET),
+	mechanics(nullptr)
+{
+	levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
+}
+
+CSpell::~CSpell()
+{
+	delete mechanics;
+}
+
+void CSpell::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
+{
+	mechanics->applyBattle(battle, packet);
+}
+
+bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
+{
+	assert(env);
+		
+	return mechanics->adventureCast(env, parameters);
+}
+
+void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
+{
+	assert(env);
+	
+	mechanics->battleCast(env, parameters);
+}
+
+bool CSpell::isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const
+{
+	if(!hasSpellBook)
+		return false;
+	
+	const bool inSpellBook = vstd::contains(spellBook, id);
+	const bool isBonus = caster->hasBonusOfType(Bonus::SPELL, id);
+	
+	bool inTome = false;
+	
+	forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
+	{
+		if(caster->hasBonusOfType(cnf.knoledgeBonus))
+		{
+			inTome = stop = true;
+		}				
+	});	
+
+    if (isSpecialSpell())
+    {
+        if (inSpellBook)
+        {//hero has this spell in spellbook
+            logGlobal->errorStream() << "Special spell in spellbook "<<name;
+        }
+        return isBonus;
+    }
+    else
+    {
+       return inSpellBook || inTome || isBonus || caster->hasBonusOfType(Bonus::SPELLS_OF_LEVEL, level);
+    }	
+}
+
+const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
+{
+	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
+	{
+		logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
+		throw new std::runtime_error("Invalid school level");
+	}
+
+	return levels.at(level);
+}
+
+ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance* caster, const CStack* affectedCreature) const
+{
+	ui32 ret = baseDamage;
+	//applying sorcery secondary skill
+	if(caster)
+	{
+		ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
+		ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id.toEnum())) / 100.0;
+		
+		forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
+		{
+			ret *= (100.0 + caster->valOfBonuses(cnf.damagePremyBonus)) / 100.0;
+			stop = true; //only bonus from one school is used
+		});		
+
+		if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
+			ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, id.toEnum()) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
+	}
+	return ret;	
+}
+
+ui32 CSpell::calculateDamage(const CGHeroInstance * 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;
+		}
+	}
+	ret = calculateBonus(ret, caster, affectedCreature);
+	return ret;	
+}
+
+std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
+{
+	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
+{
+	ISpellMechanics::SpellTargetingContext ctx(this, cb,mode,casterColor,spellLvl,destination);
+
+	std::set<const CStack* > attackedCres = mechanics->getAffectedStacks(ctx);
+	
+	//now handle immunities		
+	auto predicate = [&, this](const CStack * s)->bool
+	{
+		bool hitDirectly = ctx.ti.alwaysHitDirectly && s->coversPos(destination);
+		bool notImmune = (ESpellCastProblem::OK == isImmuneByStack(caster, s));
+		
+		return !(hitDirectly || notImmune);  
+	};	
+	vstd::erase_if(attackedCres, predicate);
+	
+	return attackedCres;
+}
+
+
+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;
+	for(const SpellSchoolInfo & cnf : SpellConfig::SCHOOL)
+	{
+		if(school.at(cnf.id))
+		{
+			cb(cnf, stop);
+			
+			if(stop)
+				break;
+		}				
+	}	
+}
+
+
+bool CSpell::isCombatSpell() const
+{
+	return combatSpell;
+}
+
+bool CSpell::isAdventureSpell() const
+{
+	return !combatSpell;
+}
+
+bool CSpell::isCreatureAbility() const
+{
+	return creatureAbility;
+}
+
+bool CSpell::isPositive() const
+{
+	return positiveness == POSITIVE;
+}
+
+bool CSpell::isNegative() const
+{
+	return positiveness == NEGATIVE;
+}
+
+bool CSpell::isNeutral() const
+{
+	return positiveness == NEUTRAL;
+}
+
+bool CSpell::isHealingSpell() const
+{
+	return isRisingSpell() || (id == SpellID::CURE);
+}
+
+bool CSpell::isRisingSpell() const
+{
+	return isRising;
+}
+
+bool CSpell::isDamageSpell() const
+{
+	return isDamage;
+}
+
+bool CSpell::isOffensiveSpell() const
+{
+	return isOffensive;
+}
+
+bool CSpell::isSpecialSpell() const
+{
+	return isSpecial;
+}
+
+bool CSpell::hasEffects() const
+{
+	return !levels[0].effects.empty();
+}
+
+const std::string& CSpell::getIconImmune() const
+{
+	return iconImmune;
+}
+
+const std::string& CSpell::getCastSound() const
+{
+	return castSound;
+}
+
+
+
+si32 CSpell::getCost(const int skillLevel) const
+{
+	return getLevelInfo(skillLevel).cost;
+}
+
+si32 CSpell::getPower(const int skillLevel) const
+{
+	return getLevelInfo(skillLevel).power;
+}
+
+si32 CSpell::getProbability(const TFaction factionId) const
+{
+	if(!vstd::contains(probabilities,factionId))
+	{
+		return defaultProbability;
+	}
+	return probabilities.at(factionId);
+}
+
+
+void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
+{
+	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
+	{
+		logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
+		return;
+	}
+
+	const std::vector<Bonus> & effects = levels[level].effects;
+
+	if(effects.empty())
+	{
+		logGlobal->errorStream() << __FUNCTION__ << " This spell ("  + name + ") has no effects for level " << level;
+		return;
+	}
+
+	lst.reserve(lst.size() + effects.size());
+
+	for(const Bonus & b : effects)
+	{
+		lst.push_back(Bonus(b));
+	}
+}
+
+ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * 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){
+		return s->coversPos(destination) && (isRisingSpell() || s->alive());
+	});
+	
+	if(!stacks.empty())
+	{
+		bool allImmune = true;
+		
+		ESpellCastProblem::ESpellCastProblem problem;		
+		
+		for(auto s : stacks)
+		{
+			ESpellCastProblem::ESpellCastProblem res = isImmuneByStack(caster,s);
+			
+			if(res == ESpellCastProblem::OK)
+			{
+				allImmune = false;
+			}
+			else
+			{
+				problem = res;
+			}
+		}
+		
+		if(allImmune)
+			return problem;
+	}
+	else //no target stack on this tile
+	{
+		if(getTargetType() == CSpell::CREATURE)
+		{
+			if(caster && mode == ECastingMode::HERO_CASTING) //TODO why???
+			{
+				const CSpell::TargetInfo ti(this, caster->getSpellSchoolLevel(this), mode);
+				
+				if(!ti.massive)
+					return ESpellCastProblem::WRONG_SPELL_TARGET;					
+			}
+			else
+			{
+ 				return ESpellCastProblem::WRONG_SPELL_TARGET;
+			}			
+		}
+	}
+
+	return ESpellCastProblem::OK;	
+}
+
+
+ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) const
+{	
+	//todo: use new bonus API
+	//1. Check absolute limiters
+	for(auto b : absoluteLimiters)
+	{
+		if (!obj->hasBonusOfType(b))
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+	}
+
+	//2. Check absolute immunities
+	for(auto b : absoluteImmunities)
+	{
+		if (obj->hasBonusOfType(b))
+			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
+		return ESpellCastProblem::NOT_DECIDED;
+		
+	//4. Check negatable limit
+	for(auto b : limiters)
+	{
+		if (!obj->hasBonusOfType(b))
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+	}
+
+
+	//5. Check negatable immunities
+	for(auto b : immunities)
+	{
+		if (obj->hasBonusOfType(b))
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+	}
+
+	//6. Check elemental immunities
+	
+	ESpellCastProblem::ESpellCastProblem tmp = ESpellCastProblem::NOT_DECIDED;
+	
+	forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
+	{
+		auto element = cnf.immunityBonus;
+		
+		if(obj->hasBonusOfType(element, 0)) //always resist if immune to all spells altogether
+		{
+			tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+			stop = true;
+		}				
+		else if(!isPositive()) //negative or indifferent
+		{
+			if((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
+			{
+				tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+				stop = true;
+			}			
+		}	
+	});
+	
+	if(tmp != ESpellCastProblem::NOT_DECIDED)
+		return tmp;
+	
+	TBonusListPtr levelImmunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
+
+	if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id)
+		|| ( levelImmunities->size() > 0  &&  levelImmunities->totalValue() >= level  &&  level))
+	{
+		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+	}
+
+	return ESpellCastProblem::NOT_DECIDED;
+}
+
+ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const
+{
+	const auto immuneResult = mechanics->isImmuneByStack(caster,obj);
+	
+	if (ESpellCastProblem::NOT_DECIDED != immuneResult) 
+		return immuneResult;
+	return ESpellCastProblem::OK;	
+}
+
+
+void CSpell::setIsOffensive(const bool val)
+{
+	isOffensive = val;
+
+	if(val)
+	{
+		positiveness = CSpell::NEGATIVE;
+		isDamage = true;
+	}
+}
+
+void CSpell::setIsRising(const bool val)
+{
+	isRising = val;
+
+	if(val)
+	{
+		positiveness = CSpell::POSITIVE;
+	}
+}
+
+void CSpell::setup()
+{
+	setupMechanics();
+}
+
+
+void CSpell::setupMechanics()
+{
+	if(nullptr != mechanics)
+	{
+		logGlobal->errorStream() << "Spell " << this->name << " mechanics already set";
+		delete mechanics;
+	}
+	
+	mechanics = ISpellMechanics::createMechanics(this);	
+}
+
+///CSpell::AnimationInfo
+CSpell::AnimationInfo::AnimationInfo()
+{
+	
+}
+
+CSpell::AnimationInfo::~AnimationInfo()
+{
+	
+}
+
+std::string CSpell::AnimationInfo::selectProjectile(const double angle) const
+{	
+	std::string res;	
+	double maximum = 0.0;
+	
+	for(const auto & info : projectile)
+	{
+		if(info.minimumAngle < angle && info.minimumAngle > maximum)
+		{
+			maximum = info.minimumAngle;
+			res = info.resourceName;
+		}
+	}
+	
+	return std::move(res);	
+}
+
+
+///CSpell::TargetInfo
+CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level)
+{
+	init(spell, level);
+}
+
+CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level, ECastingMode::ECastingMode mode)
+{
+	init(spell, level);
+	if(mode == ECastingMode::ENCHANTER_CASTING)
+	{
+		smart = true; //FIXME: not sure about that, this makes all spells smart in this mode
+		massive = true;
+	}
+	else if(mode == ECastingMode::SPELL_LIKE_ATTACK)
+	{
+		alwaysHitDirectly = true;
+	}	
+}
+
+void CSpell::TargetInfo::init(const CSpell * spell, const int level)
+{
+	auto & levelInfo = spell->getLevelInfo(level);
+
+	type = spell->getTargetType();
+	smart = levelInfo.smartTarget;
+	massive = levelInfo.range == "X";
+	onlyAlive = !spell->isRisingSpell();
+	alwaysHitDirectly = false;
+	clearAffected = levelInfo.clearAffected;
+	clearTarget = levelInfo.clearTarget;
+}
+
+
+bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
+{
+	int3 diff = pos - center;
+	if(diff.x >= -9  &&  diff.x <= 9  &&  diff.y >= -8  &&  diff.y <= 8)
+		return true;
+	else
+		return false;
+}
+
+///CSpellHandler
+CSpellHandler::CSpellHandler()
+{
+
+}
+
+std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
+{
+	using namespace SpellConfig;
+	std::vector<JsonNode> legacyData;
+
+	CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
+
+	auto readSchool = [&](JsonMap& schools, const std::string& name)
+	{
+		if (parser.readString() == "x")
+		{
+			schools[name].Bool() = true;
+		}
+	};
+
+	auto read = [&,this](bool combat, bool ability)
+	{
+		do
+		{
+			JsonNode lineNode(JsonNode::DATA_STRUCT);
+
+			const si32 id = legacyData.size();
+
+			lineNode["index"].Float() = id;
+			lineNode["type"].String() = ability ? "ability" : (combat ? "combat" : "adventure");
+
+			lineNode["name"].String() = parser.readString();
+
+			parser.readString(); //ignored unused abbreviated name
+			lineNode["level"].Float()      = parser.readNumber();
+
+			auto& schools = lineNode["school"].Struct();
+
+			readSchool(schools, "earth");
+			readSchool(schools, "water");
+			readSchool(schools, "fire");
+			readSchool(schools, "air");
+
+			auto& levels = lineNode["levels"].Struct();
+
+			auto getLevel = [&](const size_t idx)->JsonMap&
+			{
+				assert(idx < GameConstants::SPELL_SCHOOL_LEVELS);
+				return levels[LEVEL_NAMES[idx]].Struct();
+			};
+
+			auto costs = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
+			lineNode["power"].Float() = parser.readNumber();
+			auto powers = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
+
+			auto& chances = lineNode["gainChance"].Struct();
+
+			for(size_t i = 0; i < GameConstants::F_NUMBER ; i++){
+				chances[ETownType::names[i]].Float() = parser.readNumber();
+			}
+
+			auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
+
+			std::vector<std::string> descriptions;
+			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS ; i++)
+				descriptions.push_back(parser.readString());
+
+			parser.readString(); //ignore attributes. All data present in JSON
+
+			//save parsed level specific data
+			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
+			{
+				auto& level = getLevel(i);
+				level["description"].String() = descriptions[i];
+				level["cost"].Float() = costs[i];
+				level["power"].Float() = powers[i];
+				level["aiValue"].Float() = AIVals[i];
+			}
+
+			legacyData.push_back(lineNode);
+
+
+		}
+		while (parser.endLine() && !parser.isNextEntryEmpty());
+	};
+
+	auto skip = [&](int cnt)
+	{
+		for(int i=0; i<cnt; i++)
+			parser.endLine();
+	};
+
+	skip(5);// header
+	read(false,false); //read adventure map spells
+	skip(3);
+	read(true,false); //read battle spells
+	skip(3);
+	read(true,true);//read creature abilities
+
+    //TODO: maybe move to config
+	//clone Acid Breath attributes for Acid Breath damage effect
+	JsonNode temp = legacyData[SpellID::ACID_BREATH_DEFENSE];
+	temp["index"].Float() = SpellID::ACID_BREATH_DAMAGE;
+	legacyData.push_back(temp);
+
+	objects.resize(legacyData.size());
+
+	return legacyData;
+}
+
+const std::string CSpellHandler::getTypeName() const
+{
+	return "spell";
+}
+
+CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
+{
+	using namespace SpellConfig;
+
+	CSpell * spell = new CSpell();
+
+	const auto type = json["type"].String();
+
+	if(type == "ability")
+	{
+		spell->creatureAbility = true;
+		spell->combatSpell = true;
+	}
+	else
+	{
+		spell->creatureAbility = false;
+		spell->combatSpell = type == "combat";
+	}
+
+	spell->name = json["name"].String();
+
+	logGlobal->traceStream() << __FUNCTION__ << ": loading spell " << spell->name;
+
+	const auto schoolNames = json["school"];
+	
+	for(const SpellSchoolInfo & info : SpellConfig::SCHOOL)
+	{
+		spell->school[info.id] = schoolNames[info.jsonName].Bool();
+	}
+
+	spell->level = json["level"].Float();
+	spell->power = json["power"].Float();
+
+	spell->defaultProbability = json["defaultGainChance"].Float();
+
+	for(const auto & node : json["gainChance"].Struct())
+	{
+		const int chance = node.second.Float();
+
+		VLC->modh->identifiers.requestIdentifier(node.second.meta, "faction",node.first, [=](si32 factionID)
+		{
+			spell->probabilities[factionID] = chance;
+		});
+	}
+
+	auto targetType = json["targetType"].String();
+
+	if(targetType == "NO_TARGET")
+		spell->targetType = CSpell::NO_TARGET;
+	else if(targetType == "CREATURE")
+		spell->targetType = CSpell::CREATURE;
+	else if(targetType == "OBSTACLE")
+		spell->targetType = CSpell::OBSTACLE;
+	else if(targetType == "LOCATION")
+		spell->targetType = CSpell::LOCATION;
+	else 
+		logGlobal->warnStream() << "Spell " << spell->name << ". Target type " << (targetType.empty() ? "empty" : "unknown ("+targetType+")") << ". Assumed NO_TARGET.";
+
+	for(const auto & counteredSpell: json["counters"].Struct())
+		if (counteredSpell.second.Bool())
+		{
+			VLC->modh->identifiers.requestIdentifier(json.meta, counteredSpell.first, [=](si32 id)
+			{
+				spell->counteredSpells.push_back(SpellID(id));
+			});
+		}
+
+	//TODO: more error checking - f.e. conflicting flags
+	const auto flags = json["flags"];
+
+	//by default all flags are set to false in constructor
+
+	spell->isDamage = flags["damage"].Bool(); //do this before "offensive"
+
+	if(flags["offensive"].Bool())
+	{
+		spell->setIsOffensive(true);
+	}
+
+	if(flags["rising"].Bool())
+	{
+		spell->setIsRising(true);
+	}
+
+	const bool implicitPositiveness = spell->isOffensive || spell->isRising; //(!) "damage" does not mean NEGATIVE  --AVS
+
+	if(flags["indifferent"].Bool())
+	{
+		spell->positiveness = CSpell::NEUTRAL;
+	}
+	else if(flags["negative"].Bool())
+	{
+		spell->positiveness = CSpell::NEGATIVE;
+	}
+	else if(flags["positive"].Bool())
+	{
+		spell->positiveness = CSpell::POSITIVE;
+	}
+	else if(!implicitPositiveness)
+	{
+		spell->positiveness = CSpell::NEUTRAL; //duplicates constructor but, just in case
+		logGlobal->errorStream() << "No positiveness specified, assumed NEUTRAL";
+	}
+
+	spell->isSpecial = flags["special"].Bool();
+
+	auto findBonus = [&](std::string name, std::vector<Bonus::BonusType> &vec)
+	{
+		auto it = bonusNameMap.find(name);
+		if(it == bonusNameMap.end())
+		{
+			logGlobal->errorStream() << spell->name << ": invalid bonus name" << name;
+		}
+		else
+		{
+			vec.push_back((Bonus::BonusType)it->second);
+		}
+	};
+
+	auto readBonusStruct = [&](std::string name, std::vector<Bonus::BonusType> &vec)
+	{
+		for(auto bonusData: json[name].Struct())
+		{
+			const std::string bonusId = bonusData.first;
+			const bool flag = bonusData.second.Bool();
+
+			if(flag)
+				findBonus(bonusId, vec);
+		}
+	};
+
+	readBonusStruct("immunity", spell->immunities);
+	readBonusStruct("absoluteImmunity", spell->absoluteImmunities);
+	readBonusStruct("limit", spell->limiters);	
+	readBonusStruct("absoluteLimit", spell->absoluteLimiters);
+
+
+	const JsonNode & graphicsNode = json["graphics"];
+
+	spell->iconImmune = graphicsNode["iconImmune"].String();
+	spell->iconBook = graphicsNode["iconBook"].String();
+	spell->iconEffect = graphicsNode["iconEffect"].String();
+	spell->iconScenarioBonus = graphicsNode["iconScenarioBonus"].String();
+	spell->iconScroll = graphicsNode["iconScroll"].String();
+
+	const JsonNode & animationNode = json["animation"];
+	
+	auto loadAnimationQueue = [&](const std::string & jsonName, CSpell::TAnimationQueue & q)	
+	{
+		auto queueNode = animationNode[jsonName].Vector();		
+		for(const JsonNode & item : queueNode)
+		{	
+			CSpell::TAnimation newItem;
+			newItem.verticalPosition = VerticalPosition::TOP;
+			
+			if(item.getType() == JsonNode::DATA_STRING)
+				newItem.resourceName = item.String();
+			else if(item.getType() == JsonNode::DATA_STRUCT)
+			{
+				newItem.resourceName = item["defName"].String();
+				
+				auto vPosStr = item["verticalPosition"].String();
+				if("bottom" == vPosStr)
+					newItem.verticalPosition = VerticalPosition::BOTTOM;
+			}	
+			q.push_back(newItem);		
+		}
+	};
+	
+	loadAnimationQueue("affect", spell->animationInfo.affect);
+	loadAnimationQueue("cast", spell->animationInfo.cast);
+	loadAnimationQueue("hit", spell->animationInfo.hit);	
+	
+	const JsonVector & projectile = animationNode["projectile"].Vector();
+	
+	for(const JsonNode & item : projectile)
+	{
+		CSpell::ProjectileInfo info;
+		info.resourceName = item["defName"].String();
+		info.minimumAngle = item["minimumAngle"].Float();
+		
+		spell->animationInfo.projectile.push_back(info);
+	}
+
+	const JsonNode & soundsNode = json["sounds"];
+	spell->castSound = soundsNode["cast"].String();
+
+
+	//load level attributes
+
+	const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS;
+
+	for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++)
+	{
+		const JsonNode & levelNode = json["levels"][LEVEL_NAMES[levelIndex]];
+		
+		CSpell::LevelInfo & levelObject = spell->levels[levelIndex];
+
+		const si32 levelPower     = levelObject.power = levelNode["power"].Float();		
+
+		levelObject.description   = levelNode["description"].String();
+		levelObject.cost          = levelNode["cost"].Float();
+		levelObject.AIValue       = levelNode["aiValue"].Float();
+		levelObject.smartTarget   = levelNode["targetModifier"]["smart"].Bool();
+		levelObject.clearTarget   = levelNode["targetModifier"]["clearTarget"].Bool();
+		levelObject.clearAffected = levelNode["targetModifier"]["clearAffected"].Bool();			
+		levelObject.range         = levelNode["range"].String();
+		
+		for(const auto & elem : levelNode["effects"].Struct())
+		{
+			const JsonNode & bonusNode = elem.second;
+			Bonus * b = JsonUtils::parseBonus(bonusNode);
+			const bool usePowerAsValue = bonusNode["val"].isNull();
+
+			//TODO: make this work. see CSpellHandler::afterLoadFinalization()
+			//b->sid = spell->id; //for all
+
+			b->source = Bonus::SPELL_EFFECT;//for all
+
+			if(usePowerAsValue)
+				b->val = levelPower;
+
+			levelObject.effects.push_back(*b);
+		}
+		
+	}
+
+	return spell;
+}
+
+void CSpellHandler::afterLoadFinalization()
+{
+	//FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
+	for(auto spell: objects)
+	{
+		for(auto & level: spell->levels)
+			for(auto & bonus: level.effects)
+				bonus.sid = spell->id;
+		spell->setup();
+	}
+}
+
+void CSpellHandler::beforeValidate(JsonNode & object)
+{
+	//handle "base" level info
+	
+	JsonNode& levels = object["levels"];
+	JsonNode& base = levels["base"];
+	
+	auto inheritNode = [&](const std::string & name){
+		JsonUtils::inherit(levels[name],base);
+	};
+	
+	inheritNode("none");
+	inheritNode("basic");
+	inheritNode("advanced");
+	inheritNode("expert");
+
+}
+
+
+CSpellHandler::~CSpellHandler()
+{
+
+}
+
+std::vector<bool> CSpellHandler::getDefaultAllowed() const
+{
+	std::vector<bool> allowedSpells;
+	allowedSpells.resize(GameConstants::SPELLS_QUANTITY, true);
+	return allowedSpells;
+}

+ 399 - 399
lib/CSpellHandler.h → lib/spells/CSpellHandler.h

@@ -1,399 +1,399 @@
-#pragma once
-
-#include "IHandlerBase.h"
-#include "../lib/ConstTransitivePtr.h"
-#include "int3.h"
-#include "GameConstants.h"
-#include "BattleHex.h"
-#include "HeroBonus.h"
-
-
-/*
- * CSpellHandler.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-class CGObjectInstance;
-
-class CSpell;
-class ISpellMechanics;
-
-class CLegacyConfigParser;
-
-class CGHeroInstance;
-class CStack;
-
-class CBattleInfoCallback;
-class BattleInfo;
-
-struct CPackForClient;
-struct BattleSpellCast;
-
-class CGameInfoCallback;
-class CRandomGenerator;
-class CMap;
-
-struct SpellSchoolInfo
-{
-	ESpellSchool id; //backlink
-	Bonus::BonusType damagePremyBonus;
-	Bonus::BonusType immunityBonus;	
-	std::string jsonName;
-	SecondarySkill::ESecondarySkill skill;
-	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 * caster;
-	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};
-
-class DLL_LINKAGE CSpell
-{
-public:
-	
-	struct ProjectileInfo
-	{
-		///in radians. Only positive value. Negative angle is handled by vertical flip
-		double minimumAngle; 
-		
-		///resource name
-		std::string resourceName;
-		 
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & minimumAngle & resourceName; 
-		}		
-	};
-	
-	struct AnimationItem
-	{
-		std::string resourceName;
-		VerticalPosition verticalPosition;
-		
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & resourceName & verticalPosition; 
-		}		
-	};
-	
-	typedef AnimationItem TAnimation;
-	typedef std::vector<TAnimation> TAnimationQueue; 
-	
-	struct DLL_LINKAGE AnimationInfo
-	{
-		AnimationInfo();
-		~AnimationInfo();
-
-		///displayed on all affected targets. 
-		TAnimationQueue affect;
-
-		///displayed on caster.
-		TAnimationQueue cast;
-
-		///displayed on target hex. If spell was casted 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.
-		///use selectProjectile to access
-		std::vector<ProjectileInfo> projectile;
-
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & projectile & hit & cast;
-		}
-
-		std::string selectProjectile(const double angle) const;
-	} animationInfo;
-	
-public:
-	struct LevelInfo
-	{
-		std::string description; //descriptions of spell for skill level
-		si32 cost;
-		si32 power;
-		si32 AIValue;
-
-		bool smartTarget;
-		bool clearTarget;
-		bool clearAffected;
-		std::string range;
-
-		std::vector<Bonus> effects;
-
-		LevelInfo();
-		~LevelInfo();
-
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & description & cost & power & AIValue & smartTarget & range & effects;
-			h & clearTarget & clearAffected;
-		}
-	};
-
-	/** \brief Low level accessor. Don`t use it if absolutely necessary
-	 *
-	 * \param level. spell school level
-	 * \return Spell level info structure
-	 *
-	 */
-	const CSpell::LevelInfo& getLevelInfo(const int level) const;
-public:
-	enum ETargetType {NO_TARGET, CREATURE, OBSTACLE, LOCATION};
-	enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
-
-	struct TargetInfo
-	{
-		ETargetType type;
-		bool smart;
-		bool massive;
-		bool onlyAlive;
-		///no immunity on primary target (mostly spell-like attack)
-		bool alwaysHitDirectly;
-		
-		bool clearTarget;
-		bool clearAffected;
-		
-		TargetInfo(const CSpell * spell, const int level);
-		TargetInfo(const CSpell * spell, const int level, ECastingMode::ECastingMode mode);
-		
-	private:
-		void init(const CSpell * spell, const int level);
-	};
-
-	SpellID id;
-	std::string identifier; //???
-	std::string name;
-
-	si32 level;
-
-	std::map<ESpellSchool, bool> school; //todo: use this instead of separate boolean fields
-	
-	si32 power; //spell's power
-
-	std::map<TFaction, si32> probabilities; //% chance to gain for castles
-
-	bool combatSpell; //is this spell combat (true) or adventure (false)
-	bool creatureAbility; //if true, only creatures can use this spell
-	si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative
-
-	std::vector<SpellID> counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs)
-
-	CSpell();
-	~CSpell();
-	
-	bool isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const;
-	
-
-	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;
-
-	bool isPositive() const;
-	bool isNegative() const;
-	bool isNeutral() const;
-
-	bool isDamageSpell() const;
-	bool isHealingSpell() const;
-	bool isRisingSpell() const;	
-	bool isOffensiveSpell() const;
-
-	bool isSpecialSpell() const;
-
-	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;
-	
-	//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;
-	
-	//internal, for use only by Mechanics classes. applying secondary skills
-	ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
-	
-	///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
-	ui32 calculateDamage(const CGHeroInstance * 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;
-
-	si32 getCost(const int skillLevel) const;
-
-	/**
-	 * Returns spell level power, base power ignored
-	 */
-	si32 getPower(const int skillLevel) const;
-
-	si32 getProbability(const TFaction factionId) const;
-
-	/**
-	 * Calls cb for each school this spell belongs to
-	 *
-	 * Set stop to true to abort looping
-	 */	
-	void forEachSchool(const std::function<void (const SpellSchoolInfo &, bool &)> & cb) const;
-
-	/**
-	 * Returns resource name of icon for SPELL_IMMUNITY bonus
-	 */
-	const std::string& getIconImmune() const;
-
-	const std::string& getCastSound() const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & identifier & id & name & level & power
-		  & probabilities  & attributes & combatSpell & creatureAbility & positiveness & counteredSpells;
-		h & isRising & isDamage & isOffensive;
-		h & targetType;
-		h & immunities & limiters & absoluteImmunities & absoluteLimiters;
-		h & iconImmune;
-		h & defaultProbability;
-		h & isSpecial;
-		h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
-		h & levels;		
-		h & school;		
-		h & animationInfo;
-
-		if(!h.saving)
-			setup();
-	}
-	friend class CSpellHandler;
-	friend class Graphics;
-public:
-	///Server logic. Has write access to GameState via packets.
-	///May be executed on client side by (future) non-cheat-proof scripts.
-	
-	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; 
-	void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; 	
-		
-public:	
-	///Client-server logic. Has direct write access to GameState.
-	///Shall be called (only) when applying packets on BOTH SIDES
-	
-	///implementation of BattleSpellCast applying
-	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
-		
-private:
-	void setIsOffensive(const bool val);
-	void setIsRising(const bool val);
-	
-	//call this after load or deserialization. cant be done in constructor.
-	void setup();
-	void setupMechanics();
-private:
-	si32 defaultProbability;
-
-	bool isRising;
-	bool isDamage;
-	bool isOffensive;
-	bool isSpecial;
-
-	std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
-
-	ETargetType targetType;
-
-	std::vector<Bonus::BonusType> immunities; //any of these grants immunity
-	std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, can't be negated
-	std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
-	std::vector<Bonus::BonusType> absoluteLimiters; //all of them are required to be affected, can't be negated
-
-	///graphics related stuff
-
-	std::string iconImmune;
-
-	std::string iconBook;
-	std::string iconEffect;
-	std::string iconScenarioBonus;
-	std::string iconScroll;
-
-	///sound related stuff
-	std::string castSound;
-
-	std::vector<LevelInfo> levels;
-	
-	ISpellMechanics * mechanics;//(!) do not serialize
-};
-
-bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door
-
-class DLL_LINKAGE CSpellHandler: public CHandlerBase<SpellID, CSpell>
-{
-public:
-	CSpellHandler();
-	virtual ~CSpellHandler();
-
-	///IHandler base
-	std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
-	void afterLoadFinalization() override;
-	void beforeValidate(JsonNode & object) override;
-
-	/**
-	 * Gets a list of default allowed spells. OH3 spells are all allowed by default.
-	 *
-	 * @return a list of allowed spells, the index is the spell id and the value either 0 for not allowed or 1 for allowed
-	 */
-	std::vector<bool> getDefaultAllowed() const override;
-
-	const std::string getTypeName() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & objects ;
-	}
-		
-protected:
-	CSpell * loadFromJson(const JsonNode & json) override;
-};
+#pragma once
+
+#include "../IHandlerBase.h"
+#include "../ConstTransitivePtr.h"
+#include "../int3.h"
+#include "../GameConstants.h"
+#include "../BattleHex.h"
+#include "../HeroBonus.h"
+
+
+/*
+ * CSpellHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+class CGObjectInstance;
+
+class CSpell;
+class ISpellMechanics;
+
+class CLegacyConfigParser;
+
+class CGHeroInstance;
+class CStack;
+
+class CBattleInfoCallback;
+class BattleInfo;
+
+struct CPackForClient;
+struct BattleSpellCast;
+
+class CGameInfoCallback;
+class CRandomGenerator;
+class CMap;
+
+struct SpellSchoolInfo
+{
+	ESpellSchool id; //backlink
+	Bonus::BonusType damagePremyBonus;
+	Bonus::BonusType immunityBonus;	
+	std::string jsonName;
+	SecondarySkill::ESecondarySkill skill;
+	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 * caster;
+	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};
+
+class DLL_LINKAGE CSpell
+{
+public:
+	
+	struct ProjectileInfo
+	{
+		///in radians. Only positive value. Negative angle is handled by vertical flip
+		double minimumAngle; 
+		
+		///resource name
+		std::string resourceName;
+		 
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & minimumAngle & resourceName; 
+		}		
+	};
+	
+	struct AnimationItem
+	{
+		std::string resourceName;
+		VerticalPosition verticalPosition;
+		
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & resourceName & verticalPosition; 
+		}		
+	};
+	
+	typedef AnimationItem TAnimation;
+	typedef std::vector<TAnimation> TAnimationQueue; 
+	
+	struct DLL_LINKAGE AnimationInfo
+	{
+		AnimationInfo();
+		~AnimationInfo();
+
+		///displayed on all affected targets. 
+		TAnimationQueue affect;
+
+		///displayed on caster.
+		TAnimationQueue cast;
+
+		///displayed on target hex. If spell was casted 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.
+		///use selectProjectile to access
+		std::vector<ProjectileInfo> projectile;
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & projectile & hit & cast;
+		}
+
+		std::string selectProjectile(const double angle) const;
+	} animationInfo;
+	
+public:
+	struct LevelInfo
+	{
+		std::string description; //descriptions of spell for skill level
+		si32 cost;
+		si32 power;
+		si32 AIValue;
+
+		bool smartTarget;
+		bool clearTarget;
+		bool clearAffected;
+		std::string range;
+
+		std::vector<Bonus> effects;
+
+		LevelInfo();
+		~LevelInfo();
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & description & cost & power & AIValue & smartTarget & range & effects;
+			h & clearTarget & clearAffected;
+		}
+	};
+
+	/** \brief Low level accessor. Don`t use it if absolutely necessary
+	 *
+	 * \param level. spell school level
+	 * \return Spell level info structure
+	 *
+	 */
+	const CSpell::LevelInfo& getLevelInfo(const int level) const;
+public:
+	enum ETargetType {NO_TARGET, CREATURE, OBSTACLE, LOCATION};
+	enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
+
+	struct TargetInfo
+	{
+		ETargetType type;
+		bool smart;
+		bool massive;
+		bool onlyAlive;
+		///no immunity on primary target (mostly spell-like attack)
+		bool alwaysHitDirectly;
+		
+		bool clearTarget;
+		bool clearAffected;
+		
+		TargetInfo(const CSpell * spell, const int level);
+		TargetInfo(const CSpell * spell, const int level, ECastingMode::ECastingMode mode);
+		
+	private:
+		void init(const CSpell * spell, const int level);
+	};
+
+	SpellID id;
+	std::string identifier; //???
+	std::string name;
+
+	si32 level;
+
+	std::map<ESpellSchool, bool> school; //todo: use this instead of separate boolean fields
+	
+	si32 power; //spell's power
+
+	std::map<TFaction, si32> probabilities; //% chance to gain for castles
+
+	bool combatSpell; //is this spell combat (true) or adventure (false)
+	bool creatureAbility; //if true, only creatures can use this spell
+	si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative
+
+	std::vector<SpellID> counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs)
+
+	CSpell();
+	~CSpell();
+	
+	bool isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const;
+	
+
+	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;
+
+	bool isPositive() const;
+	bool isNegative() const;
+	bool isNeutral() const;
+
+	bool isDamageSpell() const;
+	bool isHealingSpell() const;
+	bool isRisingSpell() const;	
+	bool isOffensiveSpell() const;
+
+	bool isSpecialSpell() const;
+
+	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;
+	
+	//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;
+	
+	//internal, for use only by Mechanics classes. applying secondary skills
+	ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
+	
+	///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
+	ui32 calculateDamage(const CGHeroInstance * 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;
+
+	si32 getCost(const int skillLevel) const;
+
+	/**
+	 * Returns spell level power, base power ignored
+	 */
+	si32 getPower(const int skillLevel) const;
+
+	si32 getProbability(const TFaction factionId) const;
+
+	/**
+	 * Calls cb for each school this spell belongs to
+	 *
+	 * Set stop to true to abort looping
+	 */	
+	void forEachSchool(const std::function<void (const SpellSchoolInfo &, bool &)> & cb) const;
+
+	/**
+	 * Returns resource name of icon for SPELL_IMMUNITY bonus
+	 */
+	const std::string& getIconImmune() const;
+
+	const std::string& getCastSound() const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & identifier & id & name & level & power
+		  & probabilities  & attributes & combatSpell & creatureAbility & positiveness & counteredSpells;
+		h & isRising & isDamage & isOffensive;
+		h & targetType;
+		h & immunities & limiters & absoluteImmunities & absoluteLimiters;
+		h & iconImmune;
+		h & defaultProbability;
+		h & isSpecial;
+		h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
+		h & levels;		
+		h & school;		
+		h & animationInfo;
+
+		if(!h.saving)
+			setup();
+	}
+	friend class CSpellHandler;
+	friend class Graphics;
+public:
+	///Server logic. Has write access to GameState via packets.
+	///May be executed on client side by (future) non-cheat-proof scripts.
+	
+	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; 
+	void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; 	
+		
+public:	
+	///Client-server logic. Has direct write access to GameState.
+	///Shall be called (only) when applying packets on BOTH SIDES
+	
+	///implementation of BattleSpellCast applying
+	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
+		
+private:
+	void setIsOffensive(const bool val);
+	void setIsRising(const bool val);
+	
+	//call this after load or deserialization. cant be done in constructor.
+	void setup();
+	void setupMechanics();
+private:
+	si32 defaultProbability;
+
+	bool isRising;
+	bool isDamage;
+	bool isOffensive;
+	bool isSpecial;
+
+	std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
+
+	ETargetType targetType;
+
+	std::vector<Bonus::BonusType> immunities; //any of these grants immunity
+	std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, can't be negated
+	std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
+	std::vector<Bonus::BonusType> absoluteLimiters; //all of them are required to be affected, can't be negated
+
+	///graphics related stuff
+
+	std::string iconImmune;
+
+	std::string iconBook;
+	std::string iconEffect;
+	std::string iconScenarioBonus;
+	std::string iconScroll;
+
+	///sound related stuff
+	std::string castSound;
+
+	std::vector<LevelInfo> levels;
+	
+	ISpellMechanics * mechanics;//(!) do not serialize
+};
+
+bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door
+
+class DLL_LINKAGE CSpellHandler: public CHandlerBase<SpellID, CSpell>
+{
+public:
+	CSpellHandler();
+	virtual ~CSpellHandler();
+
+	///IHandler base
+	std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
+	void afterLoadFinalization() override;
+	void beforeValidate(JsonNode & object) override;
+
+	/**
+	 * Gets a list of default allowed spells. OH3 spells are all allowed by default.
+	 *
+	 * @return a list of allowed spells, the index is the spell id and the value either 0 for not allowed or 1 for allowed
+	 */
+	std::vector<bool> getDefaultAllowed() const override;
+
+	const std::string getTypeName() const override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & objects ;
+	}
+		
+protected:
+	CSpell * loadFromJson(const JsonNode & json) override;
+};

+ 9 - 9
lib/SpellMechanics.cpp → lib/spells/ISpellMechanics.cpp

@@ -9,18 +9,18 @@
  */
 
 #include "StdInc.h"
-#include "SpellMechanics.h"
+#include "ISpellMechanics.h"
 
-#include "CObstacleInstance.h"
-#include "mapObjects/CGHeroInstance.h"
-#include "BattleState.h"
-#include "CRandomGenerator.h"
+#include "../CObstacleInstance.h"
+#include "../mapObjects/CGHeroInstance.h"
+#include "../BattleState.h"
+#include "../CRandomGenerator.h"
 
-#include "NetPacks.h"
+#include "../NetPacks.h"
 
-#include "mapping/CMap.h"
-#include "CGameInfoCallback.h"
-#include "CGameState.h"
+#include "../mapping/CMap.h"
+#include "../CGameInfoCallback.h"
+#include "../CGameState.h"
 
 namespace SRSLPraserHelpers
 {

+ 1 - 1
lib/SpellMechanics.h → lib/spells/ISpellMechanics.h

@@ -11,7 +11,7 @@
 #pragma once
 
 #include "CSpellHandler.h"
-#include "BattleHex.h"
+#include "../BattleHex.h"
 
 class DLL_LINKAGE ISpellMechanics
 {

+ 1 - 1
server/CGameHandler.cpp

@@ -9,7 +9,7 @@
 #include "../lib/CArtHandler.h"
 #include "../lib/CBuildingHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/CCreatureHandler.h"

+ 1 - 1
server/CVCMIServer.cpp

@@ -12,7 +12,7 @@
 #include "../lib/CHeroHandler.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/CBuildingHandler.h"
-#include "../lib/CSpellHandler.h"
+#include "../lib/spells/CSpellHandler.h"
 #include "../lib/CCreatureHandler.h"
 #include "zlib.h"
 #include "CVCMIServer.h"