Prechádzať zdrojové kódy

Part 2 of new spell configuration
1) spell handler refactored to support modding in general way
2) imunnity icons moved to WoG as they depends on wog`s graphics
3) introduced new class template for handlers (todo: use this in other handlers)
4) save format changed
5) introduced "absolute immunity" - unaffected by "the Orb" etc. (todo: use it in config)
6) new format documented on wiki, added json schema.

* more split of registertypes - fixes 32 mingw build

alexvins 11 rokov pred
rodič
commit
4203d69525
43 zmenil súbory, kde vykonal 4543 pridanie a 1524 odobranie
  1. 1 1
      AI/BattleAI/BattleAI.cbp
  2. 12 12
      AI/BattleAI/BattleAI.cpp
  3. 1 1
      AI/EmptyAI/EmptyAI.cbp
  4. 1 1
      AI/StupidAI/StupidAI.cbp
  5. 1 1
      AI/VCAI/VCAI.cbp
  6. 57 0
      Mods/WoG/config/wog/spells.json
  7. 5 0
      Mods/WoG/mod.json
  8. 24 24
      client/CCastleInterface.cpp
  9. 9 9
      client/CCreatureWindow.cpp
  10. 9 8
      client/CMT.cpp
  11. 2 2
      client/CMusicHandler.cpp
  12. 10 10
      client/CPlayerInterface.cpp
  13. 5 5
      client/CPreGame.cpp
  14. 24 24
      client/CSpellWindow.cpp
  15. 3 3
      client/GUIClasses.cpp
  16. 1 2
      client/VCMI_client.cbp
  17. 43 43
      client/battle/CBattleInterface.cpp
  18. 11 11
      client/gui/CGuiHandler.cpp
  19. 1 0
      config/defaultMods.json
  20. 5 1
      config/gameConfig.json
  21. 5 0
      config/schemas/mod.json
  22. 206 0
      config/schemas/spell.json
  23. 3270 1061
      config/spell_info.json
  24. 3 3
      lib/BattleState.cpp
  25. 14 14
      lib/CBattleCallback.cpp
  26. 16 16
      lib/CBonusTypeHandler.h
  27. 20 16
      lib/CGameState.cpp
  28. 4 2
      lib/CModHandler.cpp
  29. 2 2
      lib/CObjectHandler.cpp
  30. 493 149
      lib/CSpellHandler.cpp
  31. 87 13
      lib/CSpellHandler.h
  32. 13 13
      lib/Connection.h
  33. 1 1
      lib/GameConstants.cpp
  34. 59 1
      lib/IHandlerBase.h
  35. 6 0
      lib/NetPacksLib.cpp
  36. 11 5
      lib/VCMI_lib.cbp
  37. 3 1
      lib/registerTypes/RegisterTypes.cpp
  38. 13 8
      lib/registerTypes/RegisterTypes.h
  39. 0 29
      lib/registerTypes/TypesMapObjects.cpp
  40. 30 0
      lib/registerTypes/TypesMapObjects1.cpp
  41. 30 0
      lib/registerTypes/TypesMapObjects2.cpp
  42. 31 31
      server/CGameHandler.cpp
  43. 1 1
      server/VCMI_server.cbp

+ 1 - 1
AI/BattleAI/BattleAI.cbp

@@ -44,7 +44,7 @@
 		<Linker>
 			<Add option="-lboost_system$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib)" />
+			<Add directory="$(#boost.lib32)" />
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="BattleAI.cpp" />

+ 12 - 12
AI/BattleAI/BattleAI.cpp

@@ -112,7 +112,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 		print("activeStack called for " + stack->nodeName());
 		if(stack->type->idNumber == CreatureID::CATAPULT)
 			return useCatapult(stack);
-		
+
 		if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->hasBonusOfType(Bonus::HEALER))
 		{
 			auto healingTargets = cb->battleGetStacks(CBattleInfoEssentials::ONLY_MINE);
@@ -343,7 +343,7 @@ SpellTypes spellType(const CSpell *spell)
 	if (spell->isOffensiveSpell())
 		return OFFENSIVE_SPELL;
 	if (spell->hasEffects())
-		return TIMED_EFFECT;	
+		return TIMED_EFFECT;
 	return OTHER;
 
 }
@@ -384,13 +384,13 @@ struct CurrentOffensivePotential
 };
 
 
-// 
+//
 // //set has its own order, so remove_if won't work. TODO - reuse for map
 // template<typename Elem, typename Predicate>
 // void erase_if(std::set<Elem> &setContainer, Predicate pred)
 // {
 // 	auto itr = setContainer.begin();
-// 	auto endItr = setContainer.end(); 
+// 	auto endItr = setContainer.end();
 // 	while(itr != endItr)
 // 	{
 // 		auto tmpItr = itr++;
@@ -409,14 +409,14 @@ void CBattleAI::attemptCastingSpell()
 
 	//Get all spells we can cast
 	std::vector<const CSpell*> possibleSpells;
-	vstd::copy_if(VLC->spellh->spells, std::back_inserter(possibleSpells), [this] (const CSpell *s) -> bool
-	{ 
-		auto problem = cbc->battleCanCastThisSpell(s); 
-		return problem == ESpellCastProblem::OK; 
+	vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [this] (const CSpell *s) -> bool
+	{
+		auto problem = cbc->battleCanCastThisSpell(s);
+		return problem == ESpellCastProblem::OK;
 	});
 	LOGFL("I can cast %d spells.", possibleSpells.size());
 
-	vstd::erase_if(possibleSpells, [](const CSpell *s) 
+	vstd::erase_if(possibleSpells, [](const CSpell *s)
 	{return spellType(s) == OTHER; });
 	LOGFL("I know about workings of %d of them.", possibleSpells.size());
 
@@ -606,7 +606,7 @@ ThreatMap::ThreatMap(const CStack *Endangered) : endangered(Endangered)
 	}
 }
 
-const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= nullptr*/, const std::string &cachingStr /*= ""*/) const 
+const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= nullptr*/, const std::string &cachingStr /*= ""*/) const
 {
 	TBonusListPtr ret = make_shared<BonusList>();
 	const TBonusListPtr originalList = stack->getAllBonuses(selector, limit, root, cachingStr);
@@ -658,7 +658,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo
 
 		curBai.attackerCount = attacker->count - attacker->countKilledByAttack(ap.damageReceived).first;
 		curBai.defenderCount = enemy->count - enemy->countKilledByAttack(ap.damageDealt).first;
-		if(!curBai.attackerCount) 
+		if(!curBai.attackerCount)
 			break;
 		//TODO what about defender? should we break? but in pessimistic scenario defender might be alive
 	}
@@ -689,7 +689,7 @@ PotentialTargets::PotentialTargets(const CStack *attacker, const HypotheticChang
 			auto bai = BattleAttackInfo(attacker, enemy, shooting);
 			bai.attackerBonuses = getValOr(state.bonusesOfStacks, bai.attacker, bai.attacker);
 			bai.defenderBonuses = getValOr(state.bonusesOfStacks, bai.defender, bai.defender);
-			
+
 			if(hex.isValid())
 			{
 				assert(dists[hex] <= attacker->Speed());

+ 1 - 1
AI/EmptyAI/EmptyAI.cbp

@@ -45,7 +45,7 @@
 		<Linker>
 			<Add option="-lboost_system$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib)" />
+			<Add directory="$(#boost.lib32)" />
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="CEmptyAI.cpp" />

+ 1 - 1
AI/StupidAI/StupidAI.cbp

@@ -47,7 +47,7 @@
 		<Linker>
 			<Add option="-lboost_system$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib)" />
+			<Add directory="$(#boost.lib32)" />
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="StdInc.h">

+ 1 - 1
AI/VCAI/VCAI.cbp

@@ -54,7 +54,7 @@
 			<Add option="-lboost_thread$(#boost.libsuffix)" />
 			<Add option="-lboost_chrono$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib)" />
+			<Add directory="$(#boost.lib32)" />
 			<Add directory="../.." />
 		</Linker>
 		<Unit filename="AIUtility.cpp" />

+ 57 - 0
Mods/WoG/config/wog/spells.json

@@ -0,0 +1,57 @@
+{
+
+        "core:implosion" :
+        {
+                "graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPIMP"
+		}
+        },
+
+        "core:meteorShower" : {
+		"graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPMET"
+		}
+	},
+	"core:armageddon" : {
+
+		"graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPARM"
+		}
+
+	},
+	"core:dispel" : {
+
+		"graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPDISP"
+		}
+
+	},
+        "core:slow" : {
+		"graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPSLOW"
+		}
+        },
+
+        "core:berserk" : {
+		"graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPBERS"
+		}
+        },
+	"core:hypnotize" : {
+                "graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPHYPN"
+		}
+        },
+
+        "core:blind" : {
+		"graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPBLIND"
+		}
+        },
+
+        "core:dispelHelpful" : {
+		"graphics" : {
+			"iconImmune" : "ZVS/LIB1.RES/E_SPDISB"
+		}
+        }
+}

+ 5 - 0
Mods/WoG/mod.json

@@ -22,6 +22,11 @@
 		"config/wog/heroClasses.json"
 	],
 
+        "spells" :
+        [
+                "config/wog/spells.json"
+        ],
+
 	"filesystem":
 	{
 		"" :

+ 24 - 24
client/CCastleInterface.cpp

@@ -264,11 +264,11 @@ CDwellingInfoBox::CDwellingInfoBox(int centerX, int centerY, const CGTownInstanc
 
 	title = new CLabel(80, 30, FONT_SMALL, CENTER, Colors::WHITE, creature->namePl);
 	animation =  new CCreaturePic(30, 44, creature, true, true);
-	
+
 	std::string text = boost::lexical_cast<std::string>(Town->creatures.at(level).first);
 	available = new CLabel(80,190, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[217] + text);
 	costPerTroop = new CLabel(80, 227, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[346]);
-	
+
 	for(int i = 0; i<GameConstants::RESOURCE_QUANTITY; i++)
 	{
 		if(creature->cost[i])
@@ -437,7 +437,7 @@ void CHeroGSlot::set(const CGHeroInstance *newHero)
 		image = new CAnimImage("PortraitsLarge", newHero->portrait, 0, 0, 0);
 	else if(!upg && owner->showEmpty) //up garrison
 		image = new CAnimImage("CREST58", LOCPLINT->castleInt->town->getOwner().getNum(), 0, 0, 0);
-	else 
+	else
 		image = nullptr;
 }
 
@@ -900,7 +900,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
 
 	recreateIcons();
 	CCS->musich->playMusic(town->town->clientInfo.musicTheme, true);
-	
+
 	bicons = CDefHandler::giveDefEss(town->town->clientInfo.buildingsIcons);
 }
 
@@ -987,7 +987,7 @@ CCreaInfo::CCreaInfo(Point position, const CGTownInstance *Town, int Level, bool
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	pos += position;
-	
+
 	if ( town->creatures.size() <= level || town->creatures[level].second.empty())
 	{
 		level = -1;
@@ -1079,7 +1079,7 @@ std::string CCreaInfo::genGrowthText()
 	{
 		descr +="\n" + entry.description;
 	}
-	
+
 	return descr;
 }
 
@@ -1275,7 +1275,7 @@ CHallInterface::CBuildingBox::CBuildingBox(int x, int y, const CGTownInstance *
 	pos.y += y;
 	pos.w = 154;
 	pos.h = 92;
-	
+
 	state = LOCPLINT->cb->canBuildStructure(town,building->bid);
 	assert(state < EBuildingState::BUILDING_ERROR);
 	static int panelIndex[10] = { 3,  3,  3, 0, 0, 2, 2,  1, 2, 2};
@@ -1301,7 +1301,7 @@ CHallInterface::CHallInterface(const CGTownInstance *Town):
 	statusBar = new CGStatusBar(new CPicture(*background, barRect, 5, 556, false));
 
 	title = new CLabel(399, 12, FONT_MEDIUM, CENTER, Colors::WHITE, town->town->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->Name());
-	exit = new CAdventureMapButton(CGI->generaltexth->hcommands[8], "", 
+	exit = new CAdventureMapButton(CGI->generaltexth->hcommands[8], "",
 	           boost::bind(&CHallInterface::close,this), 748, 556, "TPMAGE1.DEF", SDLK_RETURN);
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
@@ -1408,7 +1408,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 		          "", boost::bind(&CBuildWindow::buyFunc,this), 45, 446,"IBUY30", SDLK_RETURN);
 		buy->borderColor = Colors::METALLIC_GOLD;
 		buy->borderEnabled = true;
-		
+
 		cancel = new CAdventureMapButton(boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->Name()),
 		             "", boost::bind(&CBuildWindow::close,this), 290, 445, "ICANCEL", SDLK_ESCAPE);
 		cancel->borderColor = Colors::METALLIC_GOLD;
@@ -1437,10 +1437,10 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 	ui32 fortSize = town->creatures.size();
 	if (fortSize > GameConstants::CREATURES_PER_TOWN && town->creatures.back().second.empty())
 		fortSize--;
-	
+
 	const CBuilding *fortBuilding = town->town->buildings.at(BuildingID(town->fortLevel()+6));
 	title = new CLabel(400, 12, FONT_BIG, CENTER, Colors::WHITE, fortBuilding->Name());
-	
+
 	std::string text = boost::str(boost::format(CGI->generaltexth->fcommands[6]) % fortBuilding->Name());
 	exit = new CAdventureMapButton(text, "", boost::bind(&CFortScreen::close,this) ,748, 556, "TPMAGE1", SDLK_RETURN);
 	exit->assignedKeys.insert(SDLK_ESCAPE);
@@ -1454,7 +1454,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 		positions += Point(206,421);
 	else
 		positions += Point(10, 421), Point(404,421);
-	
+
 	for (ui32 i=0; i<fortSize; i++)
 	{
 		BuildingID buildingID;
@@ -1568,10 +1568,10 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
 	pos.y +=posY;
 	pos.w = 386;
 	pos.h = 126;
-	
+
 	if (!town->creatures[level].second.empty())
 		addUsedEvents(LCLICK | RCLICK | HOVER);//Activate only if dwelling is present
-	
+
 	icons = new CPicture("TPCAINFO", 261, 3);
 	if (getMyBuilding() != nullptr)
 	{
@@ -1640,18 +1640,18 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner):
     CWindowObject(BORDERED, "TPMAGE")
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	
+
 	window = new CPicture(owner->town->town->clientInfo.guildWindow , 332, 76);
-	
+
 	resdatabar = new CMinorResDataBar;
 	resdatabar->pos.x += pos.x;
 	resdatabar->pos.y += pos.y;
 	Rect barRect(7, 556, 737, 18);
 	statusBar = new CGStatusBar(new CPicture(*background, barRect, 7, 556, false));
-	
+
 	exit = new CAdventureMapButton(CGI->generaltexth->allTexts[593],"",boost::bind(&CMageGuildScreen::close,this), 748, 556,"TPMAGE1.DEF",SDLK_RETURN);
 	exit->assignedKeys.insert(SDLK_ESCAPE);
-	
+
 	std::vector<std::vector<Point> > positions;
 
 	positions.resize(5);
@@ -1660,14 +1660,14 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner):
 	positions[2] += Point(570,82),  Point(672,82),  Point(570,157), Point(672,157);
 	positions[3] += Point(183,42),  Point(183,148), Point(183,253);
 	positions[4] += Point(491,325), Point(591,325);
-	
+
 	for(size_t i=0; i<owner->town->town->mageLevel; i++)
 	{
 		size_t spellCount = owner->town->spellsAtLevel(i+1,false); //spell at level with -1 hmmm?
 		for(size_t j=0; j<spellCount; j++)
 		{
 			if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()>j)
-				spells.push_back( new Scroll(positions[i][j], CGI->spellh->spells[owner->town->spells[i][j]]));
+				spells.push_back( new Scroll(positions[i][j], CGI->spellh->objects[owner->town->spells[i][j]]));
 			else
 				new CAnimImage("TPMAGES.DEF", 1, 0, positions[i][j].x, positions[i][j].y);//closed scroll
 		}
@@ -1711,15 +1711,15 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
 	statusBar = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-	
+
 	animBG = new CPicture("TPSMITBK", 64, 50);
 	animBG->needRefresh = true;
 
 	const CCreature *creature = CGI->creh->creatures[creMachineID];
 	anim = new CCreatureAnim(64, 50, creature->animDefName, Rect());
 	anim->clipRect(113,125,200,150);
-	
-	title = new CLabel(165, 28, FONT_BIG, CENTER, Colors::YELLOW, 
+
+	title = new CLabel(165, 28, FONT_BIG, CENTER, Colors::YELLOW,
 	            boost::str(boost::format(CGI->generaltexth->allTexts[274]) % creature->nameSing));
 	costText = new CLabel(165, 218, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]);
 	costValue = new CLabel(165, 290, FONT_MEDIUM, CENTER, Colors::WHITE,
@@ -1727,7 +1727,7 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
 
 	std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->nameSing);
 	buy = new CAdventureMapButton(text,"",boost::bind(&CBlacksmithDialog::close, this), 42, 312,"IBUY30.DEF",SDLK_RETURN);
-	
+
 	text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->nameSing);
 	cancel = new CAdventureMapButton(text,"",boost::bind(&CBlacksmithDialog::close, this), 224, 312,"ICANCEL.DEF",SDLK_ESCAPE);
 

+ 9 - 9
client/CCreatureWindow.cpp

@@ -240,7 +240,7 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 				else
 				{
 					selectableSkill->pos = Rect (95, 256, 55, 55); //TODO: scroll
-					const Bonus *b = CGI->creh->skillRequirements[option-100].first; 
+					const Bonus *b = CGI->creh->skillRequirements[option-100].first;
 					bonusItems.push_back (new CBonusItem (genRect(0, 0, 251, 57), stack->bonusToString(b, false), stack->bonusToString(b, true), stack->bonusToGraphics(b)));
 					selectableBonuses.push_back (selectableSkill); //insert these before other bonuses
 				}
@@ -274,19 +274,19 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 
 	//handle Magic resistance separately :/
 	const IBonusBearer *temp = stack;
-	
+
 	if (battleStack)
 	{
 		temp = battleStack;
 	}
 
-	int magicResistance = temp->magicResistance(); 
-	
+	int magicResistance = temp->magicResistance();
+
 	if (magicResistance)
 	{
 		Bonus b;
 		b.type = Bonus::MAGIC_RESISTANCE;
-		
+
 		text = VLC->getBth()->bonusToString(&b,temp,false);
 		const std::string description = VLC->getBth()->bonusToString(&b,temp,true);
 		bonusItems.push_back (new CBonusItem(genRect(0, 0, 251, 57), text, description, stack->bonusToGraphics(&b)));
@@ -423,7 +423,7 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 			if (effect < graphics->spellEffectsPics->ourImages.size()) //not all effects have graphics (for eg. Acid Breath)
 			{
 				spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds."
-				boost::replace_first (spellText, "%s", CGI->spellh->spells[effect]->name);
+				boost::replace_first (spellText, "%s", CGI->spellh->objects[effect]->name);
 				int duration = battleStack->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT,effect))->turnsRemain;
 				boost::replace_first (spellText, "%d", boost::lexical_cast<std::string>(duration));
 
@@ -558,7 +558,7 @@ void CCreatureWindow::showAll(SDL_Surface * to)
 	if (upgradeOptions.size() && (type == COMMANDER_LEVEL_UP && upgradeOptions[selectedOption] >= 100)) //add frame to selected skill
 	{
 		int index = selectedOption - selectableSkills.size(); //this is screwed
-		CSDL_Ext::drawBorder(to, Rect::around(selectableBonuses[index]->pos), int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b)); 
+		CSDL_Ext::drawBorder(to, Rect::around(selectableBonuses[index]->pos), int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b));
 	}
 }
 
@@ -633,7 +633,7 @@ void CCreatureWindow::setArt(const CArtifactInstance *art)
 	}
 	else
 		artifactImage = nullptr;
-	
+
 	redraw();
 }
 
@@ -664,7 +664,7 @@ void CCreatureWindow::artifactRemoved (const ArtifactLocation &artLoc)
 	//align artifacts to remove holes
 	for (auto al : stack->artifactsWorn)
 	{
-		ArtifactPosition freeSlot = al.second.artifact->firstAvailableSlot(stack); 
+		ArtifactPosition freeSlot = al.second.artifact->firstAvailableSlot(stack);
 		if (freeSlot < al.first)
 			LOCPLINT->cb->swapArtifacts (ArtifactLocation(stack, al.first), ArtifactLocation(stack, freeSlot));
 	}

+ 9 - 8
client/CMT.cpp

@@ -115,7 +115,7 @@ void startGameFromFile(const std::string &fname)
 	}
 	catch(std::exception &e)
 	{
-		logGlobal->errorStream() << "Failed to start from the file: " + fname << ". Error: " << e.what() 
+		logGlobal->errorStream() << "Failed to start from the file: " + fname << ". Error: " << e.what()
 			<< " Falling back to main menu.";
 		GH.curInt = CGPreGame::create();
 		return;
@@ -145,7 +145,7 @@ void init()
 
 	loadDLLClasses();
 	const_cast<CGameInfo*>(CGI)->setFromLib();
-	CCS->soundh->initSpellsSounds(CGI->spellh->spells);
+	CCS->soundh->initSpellsSounds(CGI->spellh->objects);
     logGlobal->infoStream()<<"Initializing VCMI_Lib: "<<tmh.getDiff();
 
 	pomtime.getDiff();
@@ -211,7 +211,7 @@ int main(int argc, char** argv)
     std::string executablePath = argv[0];
     std::string workDir = executablePath.substr(0, executablePath.rfind('/'));
     chdir(workDir.c_str());
-    
+
     // Check for updates
     OSX_checkForUpdates();
 
@@ -244,7 +244,7 @@ int main(int argc, char** argv)
 		{
 			po::store(po::parse_command_line(argc, argv, opts), vm);
 		}
-		catch(std::exception &e) 
+		catch(std::exception &e)
 		{
             std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
 		}
@@ -322,7 +322,7 @@ int main(int argc, char** argv)
     logGlobal->infoStream() << NAME;
 
 	srand ( time(nullptr) );
-	
+
 
 	const JsonNode& video = settings["video"];
 	const JsonNode& res = video["screenRes"];
@@ -345,6 +345,7 @@ int main(int argc, char** argv)
 			logGlobal->errorStream()<<"Something was wrong: "<< SDL_GetError();
 			exit(-1);
 		}
+		GH.mainFPSmng->init(); //(!)init here AFTER SDL_Init() while using SDL for FPS management
 		atexit(SDL_Quit);
 		setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), video["fullscreen"].Bool());
 		logGlobal->infoStream() <<"\tInitializing screen: "<<pomtime.getDiff();
@@ -784,7 +785,7 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
 			SDL_QuitSubSystem(SDL_INIT_VIDEO);
 		SDL_InitSubSystem(SDL_INIT_VIDEO);
 	}
-	
+
 	if((screen = SDL_SetVideoMode(w, h, suggestedBpp, SDL_SWSURFACE|(fullscreen?SDL_FULLSCREEN:0))) == nullptr)
 	{
         logGlobal->errorStream() << "Requested screen resolution is not available (" << w << "x" << h << "x" << suggestedBpp << "bpp)";
@@ -918,9 +919,9 @@ static void listenForEvents()
 			}
 
 			continue;
-		} 
+		}
 		{
-			boost::unique_lock<boost::mutex> lock(eventsM); 
+			boost::unique_lock<boost::mutex> lock(eventsM);
 			events.push(ev);
 		}
 	}

+ 2 - 2
client/CMusicHandler.cpp

@@ -186,7 +186,7 @@ void CSoundHandler::initSpellsSounds(const std::vector< ConstTransitivePtr<CSpel
 		for(const JsonNode &node : config["spell_sounds"].Vector())
 		{
 			int spellid = node["id"].Float();
-			const CSpell *s = CGI->spellh->spells[spellid];
+			const CSpell *s = CGI->spellh->objects[spellid];
 
 			if (vstd::contains(spellSounds, s))
                 logGlobal->errorStream() << "Spell << " << spellid << " already has a sound";
@@ -425,7 +425,7 @@ void CMusicHandler::queueNext(CMusicHandler *owner, std::string setName, std::st
 	}
 	catch(std::exception &e)
 	{
-		logGlobal->errorStream() << "Failed to queue music. setName=" << setName << "\tmusicURI=" << musicURI; 
+		logGlobal->errorStream() << "Failed to queue music. setName=" << setName << "\tmusicURI=" << musicURI;
 		logGlobal->errorStream() << "Exception: " << e.what();
 	}
 }

+ 10 - 10
client/CPlayerInterface.cpp

@@ -267,7 +267,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 		if(details.result == TryMoveHero::TELEPORTATION)
 		{
 			if(adventureInt->terrain.currentPath)
-			{ 
+			{
 				assert(adventureInt->terrain.currentPath->nodes.size() >= 2);
 				std::vector<CGPathNode>::const_iterator nodesIt = adventureInt->terrain.currentPath->nodes.end() - 1;
 
@@ -491,7 +491,7 @@ void CPlayerInterface::commanderGotLevel (const CCommanderInstance * commander,
 	waitWhileDialog();
 	CCS->soundh->playSound(soundBase::heroNewLevel);
 
-	CCreatureWindow * cw = new CCreatureWindow(skills, commander, 
+	CCreatureWindow * cw = new CCreatureWindow(skills, commander,
 												[=](ui32 selection){ cb->selectionMade(selection, queryID); });
 	GH.pushInt(cw);
 }
@@ -619,11 +619,11 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
 		isAutoFightOn = true;
 		cb->registerBattleInterface(autofightingAI);
 	}
-	
+
 	//Don't wait for dialogs when we are non-active hot-seat player
 	if(LOCPLINT == this)
 		waitForAllDialogs();
-	
+
 	BATTLE_EVENT_POSSIBLE_RETURN;
 }
 
@@ -860,7 +860,7 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	BATTLE_EVENT_POSSIBLE_RETURN;
-	
+
 	std::vector<StackAttackedInfo> arg;
 	for(auto & elem : bsa)
 	{
@@ -1204,7 +1204,7 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
 void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus &bonus, bool gain )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	if(bonus.type == Bonus::NONE)	
+	if(bonus.type == Bonus::NONE)
 		return;
 
 	updateInfo(hero);
@@ -1601,9 +1601,9 @@ bool CPlayerInterface::ctrlPressed() const
 void CPlayerInterface::update()
 {
 	// Updating GUI requires locking pim mutex (that protects screen and GUI state).
-	// When ending the game, the pim mutex might be hold bo other thread, 
+	// When ending the game, the pim mutex might be hold bo other thread,
 	// that will notify us about the ending game by setting terminate_cond flag.
-	
+
 	bool acquiredTheLockOnPim = false; //for tracking whether pim mutex locking succeeded
 	while(!terminate_cond.get() && !(acquiredTheLockOnPim = pim->try_lock())) //try acquiring long until it succeeds or we are told to terminate
 		boost::this_thread::sleep(boost::posix_time::milliseconds(15));
@@ -1616,7 +1616,7 @@ void CPlayerInterface::update()
 	}
 
 	// If we are here, pim mutex has been successfully locked - let's store it in a safe RAII lock.
-	boost::unique_lock<boost::recursive_mutex> un(*pim, boost::adopt_lock); 
+	boost::unique_lock<boost::recursive_mutex> un(*pim, boost::adopt_lock);
 
 	// While mutexes were locked away we may be have stopped being the active interface
 	if(LOCPLINT != this)
@@ -2202,7 +2202,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
 	{
 		eraseCurrentPathOf(caster, false);
 	}
-	const CSpell * spell = CGI->spellh->spells[spellID];
+	const CSpell * spell = CGI->spellh->objects[spellID];
 	if (vstd::contains(CCS->soundh->spellSounds, spell))
 		CCS->soundh->playSound(CCS->soundh->spellSounds[spell]);
 }

+ 5 - 5
client/CPreGame.cpp

@@ -481,7 +481,7 @@ CGPreGame::CGPreGame()
 
 CGPreGame::~CGPreGame()
 {
-	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim); 
+	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
 	disposeGraphics();
 	if(CGP == this)
 		CGP = nullptr;
@@ -512,7 +512,7 @@ void CGPreGame::disposeGraphics()
 
 void CGPreGame::update()
 {
-	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim); 
+	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
 	if(CGP != this) //don't update if you are not a main interface
 		return;
 
@@ -3067,7 +3067,7 @@ void CMultiMode::joinTCP()
 {
 	Settings name = settings.write["general"]["playerName"];
 	name->String() = txt->text;
-	GH.pushInt(new CSimpleJoinScreen);	
+	GH.pushInt(new CSimpleJoinScreen);
 }
 
 CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer)
@@ -3448,7 +3448,7 @@ void CBonusSelection::updateBonusSelection()
 		{
 		case CScenarioTravel::STravelBonus::SPELL:
 			desc = CGI->generaltexth->allTexts[715];
-			boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2]->name);
+			boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);
 			break;
 		case CScenarioTravel::STravelBonus::MONSTER:
 			picNumber = bonDescs[i].info2 + 2;
@@ -3484,7 +3484,7 @@ void CBonusSelection::updateBonusSelection()
 			break;
 		case CScenarioTravel::STravelBonus::SPELL_SCROLL:
 			desc = CGI->generaltexth->allTexts[716];
-			boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2]->name);
+			boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);
 			break;
 		case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
 			{

+ 24 - 24
client/CSpellWindow.cpp

@@ -78,9 +78,9 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 	myInt(_myInt)
 {
 	//initializing castable spells
-	for(ui32 v=0; v<CGI->spellh->spells.size(); ++v)
+	for(ui32 v=0; v<CGI->spellh->objects.size(); ++v)
 	{
-		if( !CGI->spellh->spells[v]->creatureAbility && myHero->canCastThisSpell(CGI->spellh->spells[v]) )
+		if( !CGI->spellh->objects[v]->creatureAbility && myHero->canCastThisSpell(CGI->spellh->objects[v]) )
 			mySpells.insert(SpellID(v));
 	}
 
@@ -92,7 +92,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 
 	for(auto g : mySpells)
 	{
-		const CSpell &s = *CGI->spellh->spells[g];
+		const CSpell &s = *CGI->spellh->objects[g];
 		Uint8 *sitesPerOurTab = s.combatSpell ? sitesPerTabBattle : sitesPerTabAdv;
 
 		++sitesPerOurTab[4];
@@ -123,7 +123,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 				sitesPerTabAdv[v] = (sitesPerTabAdv[v] - 10) / 12 + 2;
 		}
 	}
-	
+
 	if(sitesPerTabBattle[4] % 12 == 0)
 		sitesPerTabBattle[4]/=12;
 	else
@@ -259,7 +259,7 @@ void CSpellWindow::fexitb()
 
 void CSpellWindow::fadvSpellsb()
 {
-	if (battleSpellsOnly == true) 
+	if (battleSpellsOnly == true)
 	{
 		turnPageRight();
 		battleSpellsOnly = false;
@@ -270,7 +270,7 @@ void CSpellWindow::fadvSpellsb()
 
 void CSpellWindow::fbattleSpellsb()
 {
-	if (battleSpellsOnly == false) 
+	if (battleSpellsOnly == false)
 	{
 		turnPageLeft();
 		battleSpellsOnly = true;
@@ -285,7 +285,7 @@ void CSpellWindow::fmanaPtsb()
 
 void CSpellWindow::selectSchool(int school)
 {
-	if (selectedTab != school) 
+	if (selectedTab != school)
 	{
 		if (selectedTab < school)
 			turnPageLeft();
@@ -299,7 +299,7 @@ void CSpellWindow::selectSchool(int school)
 
 void CSpellWindow::fLcornerb()
 {
-	if(currentPage>0) 
+	if(currentPage>0)
 	{
 		turnPageLeft();
 		--currentPage;
@@ -310,7 +310,7 @@ void CSpellWindow::fLcornerb()
 
 void CSpellWindow::fRcornerb()
 {
-	if((currentPage + 1) < (pagesWithinCurrentTab())) 
+	if((currentPage + 1) < (pagesWithinCurrentTab()))
 	{
 		turnPageRight();
 		++currentPage;
@@ -327,7 +327,7 @@ void CSpellWindow::showAll(SDL_Surface * to)
 	std::ostringstream mana;
 	mana<<myHero->mana;
 	printAtMiddleLoc(mana.str(), 435, 426, FONT_SMALL, Colors::YELLOW, to);
-	
+
 	statusBar->showAll(to);
 
 	//printing school images
@@ -363,8 +363,8 @@ class SpellbookSpellSorter
 public:
 	bool operator()(const int & a, const int & b)
 	{
-		const CSpell & A = *CGI->spellh->spells[a];
-		const CSpell & B = *CGI->spellh->spells[b];
+		const CSpell & A = *CGI->spellh->objects[a];
+		const CSpell & B = *CGI->spellh->objects[b];
 		if(A.level<B.level)
 			return true;
 		if(A.level>B.level)
@@ -394,11 +394,11 @@ void CSpellWindow::computeSpellsPerArea()
 	std::vector<SpellID> spellsCurSite;
 	for(auto it = mySpells.cbegin(); it != mySpells.cend(); ++it)
 	{
-		if(CGI->spellh->spells[*it]->combatSpell ^ !battleSpellsOnly
-			&& ((CGI->spellh->spells[*it]->air && selectedTab == 0) || 
-				(CGI->spellh->spells[*it]->fire && selectedTab == 1) ||
-				(CGI->spellh->spells[*it]->water && selectedTab == 2) ||
-				(CGI->spellh->spells[*it]->earth && selectedTab == 3) ||
+		if(CGI->spellh->objects[*it]->combatSpell ^ !battleSpellsOnly
+			&& ((CGI->spellh->objects[*it]->air && selectedTab == 0) ||
+				(CGI->spellh->objects[*it]->fire && selectedTab == 1) ||
+				(CGI->spellh->objects[*it]->water && selectedTab == 2) ||
+				(CGI->spellh->objects[*it]->earth && selectedTab == 3) ||
 				selectedTab == 4 )
 			)
 		{
@@ -611,7 +611,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 {
 	if(!down && mySpell!=-1)
 	{
-		const CSpell *sp = CGI->spellh->spells[mySpell];
+		const CSpell *sp = CGI->spellh->objects[mySpell];
 
 		int spellCost = owner->myInt->cb->getSpellCost(sp, owner->myHero);
 		if(spellCost > owner->myHero->mana) //insufficient mana
@@ -680,7 +680,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 					{
 						std::string artName = CGI->arth->artifacts[b->sid]->Name();
 						//The %s prevents %s from casting 3rd level or higher spells.
-						owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[536]) 
+						owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[536])
 							% artName % owner->myHero->name));
 					}
 					else
@@ -739,7 +739,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 						return;
 					}
 
-					if (h->getSpellSchoolLevel(CGI->spellh->spells[spell]) < 2) //not advanced or expert - teleport to nearest available city
+					if (h->getSpellSchoolLevel(CGI->spellh->objects[spell]) < 2) //not advanced or expert - teleport to nearest available city
 					{
 						int nearest = -1; //nearest town's ID
 						double dist = -1;
@@ -798,7 +798,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 	{
 		std::string dmgInfo;
 		const CGHeroInstance * hero = owner->myHero;
-		int causedDmg = owner->myInt->cb->estimateSpellDamage( CGI->spellh->spells[mySpell], (hero ? hero : nullptr));
+		int causedDmg = owner->myInt->cb->estimateSpellDamage( CGI->spellh->objects[mySpell], (hero ? hero : nullptr));
 		if(causedDmg == 0 || mySpell == 57) //Titan's Lightning Bolt already has damage info included
 			dmgInfo = "";
 		else
@@ -807,7 +807,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 			boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(causedDmg));
 		}
 
-		CRClickPopup::createAndPush(CGI->spellh->spells[mySpell]->descriptions[schoolLevel] + dmgInfo,
+		CRClickPopup::createAndPush(CGI->spellh->objects[mySpell]->descriptions[schoolLevel] + dmgInfo,
 		                            new CComponent(CComponent::spell, mySpell));
 	}
 }
@@ -820,7 +820,7 @@ void CSpellWindow::SpellArea::hover(bool on)
 		if(on)
 		{
 			std::ostringstream ss;
-			ss<<CGI->spellh->spells[mySpell]->name<<" ("<<CGI->generaltexth->allTexts[171+CGI->spellh->spells[mySpell]->level]<<")";
+			ss<<CGI->spellh->objects[mySpell]->name<<" ("<<CGI->generaltexth->allTexts[171+CGI->spellh->objects[mySpell]->level]<<")";
 			owner->statusBar->setText(ss.str());
 		}
 		else
@@ -868,7 +868,7 @@ void CSpellWindow::SpellArea::setSpell(SpellID spellID)
 	if(mySpell < 0)
 		return;
 
-	const CSpell * spell = CGI->spellh->spells[mySpell];
+	const CSpell * spell = CGI->spellh->objects[mySpell];
 	schoolLevel = owner->myHero->getSpellSchoolLevel(spell, &whichSchool);
 	spellCost = owner->myInt->cb->getSpellCost(spell, owner->myHero);
 }

+ 3 - 3
client/GUIClasses.cpp

@@ -977,7 +977,7 @@ std::string CComponent::getDescription()
 	case creature:   return "";
 	case artifact:   return CGI->arth->artifacts[subtype]->Description();
 	case experience: return CGI->generaltexth->allTexts[241];
-	case spell:      return CGI->spellh->spells[subtype]->descriptions[val];
+	case spell:      return CGI->spellh->objects[subtype]->descriptions[val];
 	case morale:     return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
 	case luck:       return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
 	case building:   return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Description();
@@ -1019,7 +1019,7 @@ std::string CComponent::getSubtitleInternal()
 			else
 				return boost::lexical_cast<std::string>(val); //amount of experience OR level required for seer hut;
 		}
-	case spell:      return CGI->spellh->spells[subtype]->name;
+	case spell:      return CGI->spellh->objects[subtype]->name;
 	case morale:     return "";
 	case luck:       return "";
 	case building:   return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Name();
@@ -4457,7 +4457,7 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
 			if(spellID >= 0)
 			{
 				if(nameStart != std::string::npos  &&  nameEnd != std::string::npos)
-					text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->spells[spellID]->name);
+					text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name);
 
 				//add spell component info (used to provide a pic in r-click popup)
 				baseType = CComponent::spell;

+ 1 - 2
client/VCMI_client.cbp

@@ -4,7 +4,6 @@
 	<Project>
 		<Option title="VCMI_client" />
 		<Option pch_mode="2" />
-		<Option default_target="Release" />
 		<Option compiler="gcc" />
 		<Build>
 			<Target title="Debug">
@@ -64,7 +63,7 @@
 			<Add option="-lSDL_mixer" />
 			<Add option="-lSDL_ttf" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib)" />
+			<Add directory="$(#boost.lib32)" />
 			<Add directory="$(#sdl.lib)" />
 			<Add directory="../" />
 		</Linker>

+ 43 - 43
client/battle/CBattleInterface.cpp

@@ -95,9 +95,9 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim)
 	animsAreDisplayed.setn(true);
 }
 
-CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, 
-								   const CGHeroInstance *hero1, const CGHeroInstance *hero2, 
-								   const SDL_Rect & myRect, 
+CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2,
+								   const CGHeroInstance *hero1, const CGHeroInstance *hero2,
+								   const SDL_Rect & myRect,
 								   shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen)
 	: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
       activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
@@ -110,7 +110,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	if(!curInt)
 	{
 		//May happen when we are defending during network MP game -> attacker interface is just not present
-		curInt = defenderInt; 
+		curInt = defenderInt;
 	}
 
 	animsAreDisplayed.setn(false);
@@ -463,7 +463,7 @@ CBattleInterface::~CBattleInterface()
 	delete bigForceField[1];
 
 	delete siegeH;
-	
+
 	//TODO: play AI tracks if battle was during AI turn
 	//if (!curInt->makingTurn)
 	//CCS->musich->playMusicFromSet(CCS->musich->aiMusics, -1);
@@ -865,7 +865,7 @@ void CBattleInterface::bAutofightf()
 {
 	if(spellDestSelectMode) //we are casting a spell
 		return;
-	
+
 	//Stop auto-fight mode
 	if(curInt->isAutoFightOn)
 	{
@@ -875,7 +875,7 @@ void CBattleInterface::bAutofightf()
 	}
 	else
 	{
-		curInt->isAutoFightOn = true;	
+		curInt->isAutoFightOn = true;
 		blockUI(true);
 
 		auto ai = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
@@ -911,7 +911,7 @@ void CBattleInterface::bSpellf()
 		auto blockingBonus = currentHero()->getBonusLocalFirst(Selector::type(Bonus::BLOCK_ALL_MAGIC));
 		if(!blockingBonus)
 			return;;
-		
+
 		if(blockingBonus->source == Bonus::ARTIFACT)
 		{
 			const int artID = blockingBonus->sid;
@@ -920,7 +920,7 @@ void CBattleInterface::bSpellf()
 			std::string heroName = myHero->hasArt(artID) ? myHero->name : enemyHero().name;
 
 			//%s wields the %s, an ancient artifact which creates a p dead to all magic.
-			LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683]) 
+			LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683])
 										% heroName % CGI->arth->artifacts[artID]->Name()));
 		}
 	}
@@ -1004,7 +1004,7 @@ void CBattleInterface::newStack(const CStack * stack)
 	creAnims[stack->ID]->pos.y = coords.y;
 	creAnims[stack->ID]->pos.w = creAnims[stack->ID]->getWidth();
 	creAnims[stack->ID]->setType(CCreatureAnim::HOLDING);
-	
+
 }
 
 void CBattleInterface::stackRemoved(int stackID)
@@ -1226,7 +1226,7 @@ void CBattleInterface::displayBattleFinished()
 
 void CBattleInterface::spellCast( const BattleSpellCast * sc )
 {
-	const CSpell &spell = *CGI->spellh->spells[sc->id];
+	const CSpell &spell = *CGI->spellh->objects[sc->id];
 
 	std::vector< std::string > anims; //for magic arrow and ice bolt
 
@@ -1342,7 +1342,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 		if(sc->castedByHero)
 		{
 			boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetHeroInfo(sc->side).name);
-			boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name); //spell name
+			boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name); //spell name
 			boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getCreature()->namePl ); //target
 		}
 		else
@@ -1452,7 +1452,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 			}
 		}
 		if (!customSpell && !sc->dmgToDisplay)
-			boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name); //simple spell name
+			boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name); //simple spell name
 		if (text.size())
 			console->addText(text);
 	}
@@ -1472,13 +1472,13 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 			//TODO artifacts that cast spell; scripts some day
 			boost::algorithm::replace_first(text, "%s", "Something");
 		}
-		boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name);
+		boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name);
 		console->addText(text);
 	}
 	if(sc->dmgToDisplay && !customSpell)
 	{
 		std::string dmgInfo = CGI->generaltexth->allTexts[376];
-		boost::algorithm::replace_first(dmgInfo, "%s", CGI->spellh->spells[sc->id]->name); //simple spell name
+		boost::algorithm::replace_first(dmgInfo, "%s", CGI->spellh->objects[sc->id]->name); //simple spell name
 		boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
 		console->addText(dmgInfo); //todo: casualties (?)
 	}
@@ -1500,8 +1500,8 @@ void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)
 	{
 		for(auto & elem : sse.stacks)
 		{
-			bool areaEffect(CGI->spellh->spells[effID]->getTargetType() == CSpell::ETargetType::NO_TARGET);
-			displayEffect(CGI->spellh->spells[effID]->mainEffectAnim, curInt->cb->battleGetStackByID(elem)->position, areaEffect);
+			bool areaEffect(CGI->spellh->objects[effID]->getTargetType() == CSpell::ETargetType::NO_TARGET);
+			displayEffect(CGI->spellh->objects[effID]->mainEffectAnim, curInt->cb->battleGetStackByID(elem)->position, areaEffect);
 		}
 	}
 	else if (sse.stacks.size() == 1 && sse.effect.size() == 2)
@@ -1545,7 +1545,7 @@ void CBattleInterface::castThisSpell(int spellID)
 	//choosing possible tragets
 	const CGHeroInstance * castingHero = (attackingHeroInstance->tempOwner == curInt->playerID) ? attackingHeroInstance : defendingHeroInstance;
 	assert(castingHero); // code below assumes non-null hero
-	sp = CGI->spellh->spells[spellID];
+	sp = CGI->spellh->objects[spellID];
 	spellSelMode = ANY_LOCATION;
 	if(sp->getTargetType() == CSpell::CREATURE)
 	{
@@ -1755,7 +1755,7 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack)
 				BonusList spellBonuses = *stack->getBonuses (Selector::type(Bonus::SPELLCASTER));
 				for (Bonus * spellBonus : spellBonuses)
 				{
-					spell = CGI->spellh->spells[spellBonus->subtype];
+					spell = CGI->spellh->objects[spellBonus->subtype];
 					switch (spellBonus->subtype)
 					{
 						case SpellID::REMOVE_OBSTACLE:
@@ -1856,7 +1856,7 @@ void CBattleInterface::endAction(const BattleAction* action)
 	queue->update();
 
 	if(tacticsMode) //stack ended movement in tactics phase -> select the next one
-		bTacticNextStack(stack); 
+		bTacticNextStack(stack);
 
 	if( action->actionType == Battle::HERO_SPELL) //we have activated next stack after sending request that has been just realized -> blockmap due to movement has changed
 		redrawBackgroundWithHexes(activeStack);
@@ -2096,10 +2096,10 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 	if(!myTurn) //we are not permit to do anything
 		return;
 
-	// This function handles mouse move over hexes and l-clicking on them. 
+	// This function handles mouse move over hexes and l-clicking on them.
 	// First we decide what happens if player clicks on this hex and set appropriately
 	// consoleMsg, cursorFrame/Type and prepare lambda realizeAction.
-	// 
+	//
 	// Then, depending whether it was hover/click we either call the action or set tooltip/cursor.
 
 	//used when hovering -> tooltip message and cursor to be set
@@ -2107,7 +2107,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 	bool setCursor = true; //if we want to suppress setting cursor
 	ECursor::ECursorTypes cursorType = ECursor::COMBAT;
 	int cursorFrame = ECursor::COMBAT_POINTER; //TODO: is this line used?
-	
+
 	//used when l-clicking -> action to be called upon the click
 	std::function<void()> realizeAction;
 
@@ -2123,7 +2123,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 	bool ourStack = false;
 	if (shere)
 		ourStack = shere->owner == curInt->playerID;
-	
+
 	//stack changed, update selection border
 	if (shere != mouseHoveredStack)
 	{
@@ -2137,9 +2137,9 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 	{
 		bool legalAction = false; //this action is legal and can be performed
 		bool notLegal = false; //this action is not legal and should display message
-		
+
 		switch (action)
-		{ 
+		{
 			case CHOOSE_TACTICS_STACK:
 				if (shere && ourStack)
 					legalAction = true;
@@ -2191,7 +2191,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				if (isCastingPossibleHere (sactive, shere, myNumber)) //need to be called before sp is determined
 				{
 					bool rise = false; //TODO: can you imagine rising hostile creatures?
-					sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo];
+					sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo];
 					if (sp && sp->isRisingSpell())
 							rise = true;
 					if (shere && (shere->alive() || rise) && ourStack)
@@ -2221,7 +2221,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				if (creatureCasting)
 					skill = sactive->valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, SpellID::TELEPORT));
 				else
-					skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->spells[spellToCast->additionalInfo]); 
+					skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->objects[spellToCast->additionalInfo]);
 				//TODO: explicitely save power, skill
 				if (curInt->cb->battleCanTeleportTo(selectedStack, myNumber, skill))
 					legalAction = true;
@@ -2247,7 +2247,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 					auto tilesThatMustBeClear = sp->rangeInHexes(myNumber, hero->getSpellSchoolLevel(sp), side, &hexesOutsideBattlefield);
 					for(BattleHex hex : tilesThatMustBeClear)
 					{
-						if(curInt->cb->battleGetStackByPos(hex, false)  ||  !!curInt->cb->battleGetObstacleOnPos(hex, false) 
+						if(curInt->cb->battleGetStackByPos(hex, false)  ||  !!curInt->cb->battleGetObstacleOnPos(hex, false)
 						 || !hex.isAvailable())
 						{
 							legalAction = false;
@@ -2377,7 +2377,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				break;
 			case HOSTILE_CREATURE_SPELL:
 			case FRIENDLY_CREATURE_SPELL:
-				sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
+				sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
 				consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s
 				switch (sp->id)
 				{
@@ -2390,7 +2390,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				isCastingPossible = true;
 				break;
 			case ANY_LOCATION:
-				sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
+				sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
 				consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
 				isCastingPossible = true;
 				break;
@@ -2412,7 +2412,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				break;
 			case SACRIFICE:
 				consoleMsg = (boost::format(CGI->generaltexth->allTexts[549]) % shere->getName()).str(); //sacrifice the %s
-				cursorFrame = ECursor::COMBAT_SACRIFICE; 
+				cursorFrame = ECursor::COMBAT_SACRIFICE;
 				spellToCast->selectedStack = shere->ID; //sacrificed creature is selected
 				isCastingPossible = true;
 				break;
@@ -2487,7 +2487,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 					consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
 				break;
 		}
-		
+
 		realizeAction = [=]
 		{
 			if (secondaryTarget) //select that target now
@@ -2495,7 +2495,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				possibleActions.clear();
 				switch (sp->id.toEnum())
 				{
-					case SpellID::TELEPORT: //don't cast spell yet, only select target		
+					case SpellID::TELEPORT: //don't cast spell yet, only select target
 						possibleActions.push_back (TELEPORT);
 						spellToCast->selectedStack = selectedStack->ID;
 						break;
@@ -2548,14 +2548,14 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 		}
 		if(eventType == LCLICK && realizeAction)
 		{
-			//opening creature window shouldn't affect myTurn... 
+			//opening creature window shouldn't affect myTurn...
 			if ((currentAction != CREATURE_INFO) && !secondaryTarget)
 			{
 				myTurn = false; //tends to crash with empty calls
 			}
 			realizeAction();
 			if (!secondaryTarget) //do not replace teleport or sacrifice cursor
-				CCS->curh->changeGraphic(ECursor::COMBAT, ECursor::COMBAT_POINTER); 
+				CCS->curh->changeGraphic(ECursor::COMBAT, ECursor::COMBAT_POINTER);
 			this->console->alterText("");
 		}
 	};
@@ -2566,7 +2566,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber)
 {
 	creatureCasting = stackCanCastSpell && !spellDestSelectMode; //TODO: allow creatures to cast aimed spells
-							
+
 	bool isCastingPossible = true;
 
 	int spellID = -1;
@@ -2579,8 +2579,8 @@ bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CSta
 		spellID  = spellToCast->additionalInfo;
 
 	sp = nullptr;
-	if (spellID >= 0) 
-		sp = CGI->spellh->spells[spellID];
+	if (spellID >= 0)
+		sp = CGI->spellh->objects[spellID];
 
 	if (sp)
 	{
@@ -2846,7 +2846,7 @@ void CBattleInterface::requestAutofightingAIToTakeAction()
 {
 	assert(curInt->isAutoFightOn);
 
-	boost::thread aiThread([&] 
+	boost::thread aiThread([&]
 	{
 		auto ba = new BattleAction(curInt->autofightingAI->activeStack(activeStack));
 
@@ -2874,7 +2874,7 @@ void CBattleInterface::requestAutofightingAIToTakeAction()
 			activateStack();
 		}
 	});
-	
+
 	aiThread.detach();
 }
 
@@ -3116,7 +3116,7 @@ void CBattleInterface::showHighlightedHexes(SDL_Surface * to)
 				if(spellToCast) //when casting spell
 				{
 					//calculating spell school level
-					const CSpell & spToCast =  *CGI->spellh->spells[spellToCast->additionalInfo];
+					const CSpell & spToCast =  *CGI->spellh->objects[spellToCast->additionalInfo];
 					ui8 schoolLevel = 0;
 
 					auto caster = activeStack->attackerOwned ? attackingHeroInstance : defendingHeroInstance;
@@ -3304,7 +3304,7 @@ void CBattleInterface::showAliveStacks(SDL_Surface * to, std::vector<const CStac
 		int pos = 0;
 		for(auto & spellId : stack->activeSpells())
 		{
-			pos += CGI->spellh->spells[ spellId ]->positiveness;
+			pos += CGI->spellh->objects[ spellId ]->positiveness;
 		}
 		return pos;
 	};

+ 11 - 11
client/gui/CGuiHandler.cpp

@@ -43,14 +43,14 @@ SSetCaptureState::~SSetCaptureState()
 	GH.defActionsDef = prevActions;
 }
 
-static inline void 
+static inline void
 processList(const ui16 mask, const ui16 flag, std::list<CIntObject*> *lst, std::function<void (std::list<CIntObject*> *)> cb)
 {
 	if (mask & flag)
-		cb(lst);	
+		cb(lst);
 }
 
-void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb) 
+void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb)
 {
 	processList(CIntObject::LCLICK,activityFlag,&lclickable,cb);
 	processList(CIntObject::RCLICK,activityFlag,&rclickable,cb);
@@ -58,14 +58,14 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
 	processList(CIntObject::MOVE,activityFlag,&motioninterested,cb);
 	processList(CIntObject::KEYBOARD,activityFlag,&keyinterested,cb);
 	processList(CIntObject::TIME,activityFlag,&timeinterested,cb);
-	processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);	
-	processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,cb);	
+	processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);
+	processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,cb);
 }
 
 void CGuiHandler::handleElementActivate(CIntObject * elem, ui16 activityFlag)
 {
 	processLists(activityFlag,[&](std::list<CIntObject*> * lst){
-		lst->push_front(elem);		
+		lst->push_front(elem);
 	});
 	elem->active_m |= activityFlag;
 }
@@ -75,7 +75,7 @@ void CGuiHandler::handleElementDeActivate(CIntObject * elem, ui16 activityFlag)
 	processLists(activityFlag,[&](std::list<CIntObject*> * lst){
 		auto hlp = std::find(lst->begin(),lst->end(),elem);
 		assert(hlp != lst->end());
-		lst->erase(hlp);		
+		lst->erase(hlp);
 	});
 	elem->active_m &= ~activityFlag;
 }
@@ -105,7 +105,7 @@ void CGuiHandler::pushInt( IShowActivatable *newInt )
 	assert(boost::range::find(listInt, newInt) == listInt.end()); // do not add same object twice
 
 	//a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway)
-	screenBuf = screen2; 
+	screenBuf = screen2;
 
 	if(!listInt.empty())
 		listInt.front()->deactivate();
@@ -140,7 +140,7 @@ IShowActivatable * CGuiHandler::topInt()
 {
 	if(listInt.empty())
 		return nullptr;
-	else 
+	else
 		return listInt.front();
 }
 
@@ -345,7 +345,7 @@ void CGuiHandler::simpleRedraw()
 }
 
 void CGuiHandler::handleMoveInterested( const SDL_MouseMotionEvent & motion )
-{	
+{
 	//sending active, MotionInterested objects mouseMoved() call
 	std::list<CIntObject*> miCopy = motioninterested;
 	for(auto & elem : miCopy)
@@ -407,7 +407,7 @@ CGuiHandler::CGuiHandler()
 
 	// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate
 	mainFPSmng = new CFramerateManager(48);
-	mainFPSmng->init(); // resets internal clock, needed for FPS manager
+	//do not init CFramerateManager here --AVS
 }
 
 CGuiHandler::~CGuiHandler()

+ 1 - 0
config/defaultMods.json

@@ -8,6 +8,7 @@
 		"creature"   : 150,
 		"faction"    : 9,
 		"hero"       : 156,
+                "spell"      : 81,
 		"mapVersion" : 28 // max supported version, SoD
 	},
 

+ 5 - 1
config/gameConfig.json

@@ -59,5 +59,9 @@
 	[
 		"config/bonuses.json",
 		"config/bonuses_texts.json"
-	]
+	],
+        "spells" :
+        [
+                "config/spell_info.json"
+        ]
 }

+ 5 - 0
config/schemas/mod.json

@@ -72,6 +72,11 @@
 			"description": "List of configuration files for heroes",
 			"items": { "type":"string", "format" : "textFile" }
 		},
+                "spells": {
+			"type":"array",
+			"description": "List of configuration files for spells",
+			"items": { "type":"string", "format" : "textFile" }
+		},
 
 		"filesystem": {
 			"type":"object",

+ 206 - 0
config/schemas/spell.json

@@ -0,0 +1,206 @@
+{
+
+	"type":"object",
+	"$schema": "http://json-schema.org/draft-04/schema",
+
+	"title" : "VCMI spell format",
+	"description" : "Format used to define new spells in VCMI",
+
+
+        "definitions" : {
+            "flags" :{
+                "type" : "object",
+                "additionalProperties" : {
+	            "type":"boolean"
+                }
+            },
+            "levelInfo":{
+                    "type": "object",
+                    "required":["range","description","cost","power","aiValue","range"],
+
+                    "additionalProperties" : false,
+		    "properties":{
+                            "description":{
+                                                 "type": "string",
+                                                 "description": "Localizable description. Use {xxx} for formatting"
+                            },
+                            "cost":{
+                                                 "type": "number",
+                                                 "description":"Cost in mana points"
+                            },
+                            "power":{
+                                                 "type": "number",
+                            },
+                            "aiValue":{
+                                                 "type": "number",
+                            },
+
+                            "range":{
+                                                 "type": "string",
+                                                 "description": "spell range description in SRSL"
+                            },
+                            "effects":{
+                                                 "type": "object",
+                                                 "description": "Timed effects",
+                                                 "additionalProperties" : {
+                                                     "$ref" : "vcmi:bonus"
+                                                 }
+                            }
+
+                    }
+            }
+        },
+
+        "required" : ["type", "name", "school", "level", "power","gainChance","flags","levels"],
+        "additionalProperties" : false,
+
+        "properties": {
+                "index":{
+                        "type": "number",
+                        "description": "numeric id of spell required only for original spells, prohibited for new spells"
+                },
+                "type":{
+                        "type": "string",
+                        "enum": ["adventure", "combat", "ability"],
+                        "description":"Spell type"
+                },
+                "name":{
+                        "type": "string",
+                        "description": "Localizable name"
+
+                },
+                "school":{
+                        "type": "object",
+                        "description": "Spell schools",
+                        "additionalProperties": false,
+
+                        "properties":{
+
+                                "air":{"type": "boolean"},
+                                "fire":{"type": "boolean"},
+                                "earth":{"type": "boolean"},
+                                "water":{"type": "boolean"}
+                        }
+
+                },
+                "level":{
+                        "type": "number",
+                        "description": "Spell level",
+                        "minimum" : 0,
+                        "maximum" : 5
+                },
+
+                "power":{
+                        "type": "number",
+                        "description": "Base power",
+                },
+
+                "defaultGainChance":{
+                        "type": "nomber",
+                        "description": "Gain chance by default for all factions"
+
+                },
+
+                "gainChance":{
+                        "type": "object",
+                        "description": "Chance in % to gain for faction. NOTE: this field is merged with faction config",
+                        "additionalProperties" : {
+			     "type": "number",
+			     "minimum" : 0
+		        }
+                },
+                "targetType":{
+                          "type": "string",
+                          "enum": ["NO_TARGET","CREATURE","OBSTACLE","CREATURE_EXPERT_MASSIVE"]
+                },
+                "anim":{
+                        "type": "number",
+                        "description": "Main effect animation (AC format), -1 - none",
+                        "minimum": -1
+                },
+                "counters":{
+                         "$ref" : "#/definitions/flags",
+                         "description": "Flags structure ids of countering spells"
+                },
+                "flags":{
+                        "type": "object",
+                        "description": "Various flags",
+                        "additionalProperties" : false,
+                        "properties":{
+                                "indifferent": {
+                                        "type":"boolean",
+                                        "description": "Spell is indifferent for target"
+                                },
+                                "negative": {
+                                        "type":"boolean",
+                                        "description": "Spell is negative for target"
+                                },
+                                "positive": {
+                                        "type":"boolean",
+                                        "description": "Spell is positive for target"
+                                },
+                                "damage": {
+                                        "type":"boolean",
+                                        "description": "Spell does damage (direct or indirect)"
+                                },
+                                "offensive": {
+                                        "type":"boolean",
+                                        "description": "Spell does direct damage (implicitly sets damage and negative)"
+                                },
+                                "rising":{
+                                        "type":"boolean",
+                                        "description": "Rising spell (implicitly sets positive)"
+                                }
+                        }
+                },
+                "immunity":{
+                        "$ref" : "#/definitions/flags",
+                         "description": "flags structure of bonus names, any one of these bonus grants immunity"
+                },
+                "absoluteImmunity":{
+                         "$ref" : "#/definitions/flags",
+                         "description": "flags structure of bonus names. Any one of these bonus grants immunity, cant be negated"
+                },
+                "limit":{
+                         "$ref" : "#/definitions/flags",
+                         "description": "flags structure of bonus names, presence of all bonuses required to be affected by"
+                },
+
+                "graphics":{
+                         "type": "object",
+                         "additionalProperties" : false,
+                         "properties":{
+                                 "iconImmune":{
+                                      "type":"string",
+                                      "description":"Resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)",
+                                      "format" : "imageFile"
+                                 }
+                         }
+
+                },
+
+                "levels":{
+                         "type": "object",
+                         "additionalProperties" : false,
+                         "required" : ["none", "basic", "advanced", "expert"],
+
+                         "properties":{
+                                 "none":{
+                                      "$ref" : "#/definitions/levelInfo"
+                                 },
+                                 "basic":{
+                                      "$ref" : "#/definitions/levelInfo"
+                                 },
+                                 "advanced":{
+                                      "$ref" : "#/definitions/levelInfo"
+                                 },
+                                 "expert":{
+                                      "$ref" : "#/definitions/levelInfo"
+                                 }
+                         }
+
+
+
+                }
+        }
+}

+ 3270 - 1061
config/spell_info.json

@@ -1,1075 +1,3284 @@
 {
-    // http://wiki.vcmi.eu/index.php?title=Spell_Format
-	
-    // Additional spell info, not included in original heroes III files
-    //   id: numeric id of spell
-    //   effect: -1 -> spell is negative for influenced creatures,
-    //			  0 -> spell is indifferent for them
-    //			  1 -> spell is positive for them
-    //   anim: main effect animation (AC format), -1 - none
-    //   ranges: spell range description in SRSL ([no magic] [basic] [advanced] [expert])
-    //	 counters: array of ids of countering spells
-    //
-    // flags: string array of
-    //		damage - ATM used only in CBattleInfoCallback::calculateSpellDmg
-    //		offensive
-    //		rising
-    //		summoning //todo:
-    //
-    //effects: array of structure for bonuses for permanent effects
-    // {bonus format} - effect //todo
-    // + values: [4 int values] (OPTIONAL default from sptraits) values for levels
-    // + ainfos: [4 int values] (optional) additional infos for levels (atm only CURSE)
-    //
-    //
-    //
-    // 	immunity: string array of bonus names, any one of these bonus grants immunity
-    //  limit: string array of bonus names, presence of all bonuses required to be affected by
-    //
-    //	graphics - OPTIONAL; object;
-    //  iconImmune - OPTIONAL; string; resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)
-
-	"spells":
-	{
-		"summonBoat"     :
-		{
-			"id": 0,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"scuttleBoat"    :
-		{
-			"id": 1,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"visions"        :
-		{
-			"id": 2,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"viewEarth"      :
-		{
-			"id": 3,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"disguise"       :
-		{
-			"id": 4,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"viewAir"        :
-		{
-			"id": 5,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"fly"            :
-		{
-			"id": 6,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"waterWalk"      :
-		{
-			"id": 7,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"dimensionDoor"  :
-		{
-			"id": 8,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"townPortal"     :
-		{
-			"id": 9,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"quicksand"      :
-		{
-			"id": 10,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"landMine"       :
-		{
-			"id": 11,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ],
-			"flags" : ["damage"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
+	"summonBoat" : {
+		"index" : 0,
+		"anim" : -1,
+		"levels" :{
+                        "none":{
+				"range" : "X"
+			},
+                        "basic":{
+				"range" : "X"
+			},
+                        "advanced":{
+				"range" : "X"
+			},
+                        "expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"scuttleBoat" : {
+		"index" : 1,
+		"anim" : -1,
+		"levels" :
+                {
+                        "none":	{
+				"range" : "X"
+			},
+                        "basic":{
+				"range" : "X"
+			},
+                        "advanced":{
+				"range" : "X"
+			},
+                        "expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"visions" : {
+		"index" : 2,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"viewEarth" : {
+		"index" : 3,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"disguise" : {
+		"index" : 4,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"viewAir" : {
+		"index" : 5,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"fly" : {
+		"index" : 6,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"waterWalk" : {
+		"index" : 7,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"dimensionDoor" : {
+		"index" : 8,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"townPortal" : {
+		"index" : 9,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"quicksand" : {
+		"index" : 10,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"landMine" : {
+		"index" : 11,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"indifferent": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"forceField" : {
+		"index" : 12,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"fireWall" : {
+		"index" : 13,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"indifferent": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"earthquake" : {
+		"index" : 14,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"magicArrow" : {
+		"index" : 15,
+		"anim" : 64,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"iceBolt" : {
+		"index" : 16,
+		"anim" : 46,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"lightningBolt" : {
+		"index" : 17,
+		"anim" : 38,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"implosion" : {
+		"index" : 18,
+		"anim" : 10,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
 
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"chainLightning" : {
+		"index" : 19,
+		"anim" : 38,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		}
+	},
+	"frostRing" : {
+		"index" : 20,
+		"anim" : 45,
+		"levels" : {
+			"none":{
+				"range" : "1"
+			},
+			"basic":{
+				"range" : "1"
+			},
+			"advanced":{
+				"range" : "1"
+			},
+			"expert":{
+				"range" : "1"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"fireball" : {
+		"index" : 21,
+		"anim" : 53,
+		"levels" : {
+			"none":{
+				"range" : "0,1"
+			},
+			"basic":{
+				"range" : "0,1"
+			},
+			"advanced":{
+				"range" : "0,1"
+			},
+			"expert":{
+				"range" : "0,1"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"inferno" : {
+		"index" : 22,
+		"anim" : 9,
+		"levels" : {
+			"none":{
+				"range" : "0-2"
+			},
+			"basic":{
+				"range" : "0-2"
+			},
+			"advanced":{
+				"range" : "0-2"
+			},
+			"expert":{
+				"range" : "0-2"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"meteorShower" : {
+		"index" : 23,
+		"anim" : 16,
+		"levels" : {
+			"none":{
+				"range" : "0,1"
+			},
+			"basic":{
+				"range" : "0,1"
+			},
+			"advanced":{
+				"range" : "0,1"
+			},
+			"expert":{
+				"range" : "0,1"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"deathRipple" : {
+		"index" : 24,
+		"anim" : 8,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true,
+			"UNDEAD": true,
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"destroyUndead" : {
+		"index" : 25,
+		"anim" : 29,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"limit" : {
+			"UNDEAD": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"armageddon" : {
+		"index" : 26,
+		"anim" : 12,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"shield" : {
+		"index" : 27,
+		"anim" : 27,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"airShield" : {
+		"index" : 28,
+		"anim" : 2,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"fireShield" : {
+		"index" : 29,
+		"anim" : 11,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"fireShield" : {
+						"type" : "FIRE_SHIELD",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"fireShield" : {
+						"type" : "FIRE_SHIELD",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"fireShield" : {
+						"type" : "FIRE_SHIELD",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"fireShield" : {
+						"type" : "FIRE_SHIELD",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectAir" : {
+		"index" : 30,
+		"anim" : 22,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectFire" : {
+		"index" : 31,
+		"anim" : 24,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectWater" : {
+		"index" : 32,
+		"anim" : 23,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 2,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 2,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 2,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 2,
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectEarth" : {
+		"index" : 33,
+		"anim" : 26,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 3,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 3,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 3,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 3,
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"antiMagic" : {
+		"index" : 34,
+		"anim" : 5,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"levelSpellImmunity" : {
+						"val" : 3,
+						"type" : "LEVEL_SPELL_IMMUNITY",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"levelSpellImmunity" : {
+						"val" : 3,
+						"type" : "LEVEL_SPELL_IMMUNITY",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"levelSpellImmunity" : {
+						"val" : 4,
+						"type" : "LEVEL_SPELL_IMMUNITY",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"levelSpellImmunity" : {
+						"val" : 5,
+						"type" : "LEVEL_SPELL_IMMUNITY",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"dispel" : {
+		"index" : 35,
+		"anim" : 41,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"magicMirror" : {
+		"index" : 36,
+		"anim" : 3,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"magicMirror" : {
+						"type" : "MAGIC_MIRROR",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"magicMirror" : {
+						"type" : "MAGIC_MIRROR",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"magicMirror" : {
+						"type" : "MAGIC_MIRROR",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"magicMirror" : {
+						"type" : "MAGIC_MIRROR",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"cure" : {
+		"index" : 37,
+		"anim" : 39,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"resurrection" : {
+		"index" : 38,
+		"anim" : 79,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"rising": true,
+			"positive": true
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		}
+	},
+	"animateDead" : {
+		"index" : 39,
+		"anim" : 79,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"rising": true,
+			"positive": true
+		},
+		"limit" : {
+			"UNDEAD": true
+		}
+	},
+	"sacrifice" : {
+		"index" : 40,
+		"anim" : 79,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"rising": true,
+			"positive": true
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		}
+	},
+	"bless" : {
+		"index" : 41,
+		"anim" : 36,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"alwaysMaximumDamage" : {
+						"val" : 0,
+						"type" : "ALWAYS_MAXIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"alwaysMaximumDamage" : {
+						"val" : 0,
+						"type" : "ALWAYS_MAXIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"alwaysMaximumDamage" : {
+						"val" : 1,
+						"type" : "ALWAYS_MAXIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"alwaysMaximumDamage" : {
+						"val" : 1,
+						"type" : "ALWAYS_MAXIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.curse": true
+		},
+		"immunity" : {
+			"UNDEAD": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"curse" : {
+		"index" : 42,
+		"anim" : 40,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"alwaysMinimumDamage" : {
+						"addInfo" : 0,
+						"val" : 0,
+						"type" : "ALWAYS_MINIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"alwaysMinimumDamage" : {
+						"addInfo" : 0,
+						"val" : 0,
+						"type" : "ALWAYS_MINIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"alwaysMinimumDamage" : {
+						"addInfo" : 20,
+						"val" : 1,
+						"type" : "ALWAYS_MINIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"alwaysMinimumDamage" : {
+						"addInfo" : 20,
+						"val" : 1,
+						"type" : "ALWAYS_MINIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.bless": true
+		},
+		"immunity" : {
+			"UNDEAD": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"bloodlust" : {
+		"index" : 43,
+		"anim" : 4,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_MELEE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_MELEE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_MELEE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_MELEE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.weakness": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"precision" : {
+		"index" : 44,
+		"anim" : 25,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_DISTANCE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_DISTANCE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_DISTANCE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_DISTANCE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"limit" : {
+			"SHOOTER": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"weakness" : {
+		"index" : 45,
+		"anim" : 56,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.bloodlust": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"stoneSkin" : {
+		"index" : 46,
+		"anim" : 54,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"disruptingRay" : {
+		"index" : 47,
+                "targetType" : "CREATURE", //fix, dont remove
+		"anim" : 14,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"valueType" : "ADDITIVE_VALUE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"valueType" : "ADDITIVE_VALUE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -4,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"valueType" : "ADDITIVE_VALUE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -5,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"valueType" : "ADDITIVE_VALUE",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"prayer" : {
+		"index" : 48,
+		"anim" : 0,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"attack" : {
+						"val" : 2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : 2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					},
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 2,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"attack" : {
+						"val" : 2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : 2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					},
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 2,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"attack" : {
+						"val" : 4,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : 4,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					},
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 4,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"attack" : {
+						"val" : 4,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : 4,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					},
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 4,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"mirth" : {
+		"index" : 49,
+		"anim" : 20,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"morale" : {
+						"val" : 1,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"morale" : {
+						"val" : 1,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"morale" : {
+						"val" : 2,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"morale" : {
+						"val" : 2,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.sorrow":true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"sorrow" : {
+		"index" : 50,
+		"anim" : 30,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"morale" : {
+						"val" : -1,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"morale" : {
+						"val" : -1,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"morale" : {
+						"val" : -2,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"morale" : {
+						"val" : -2,
+						"type" : "MORALE",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.mirth":true
+		},
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"fortune" : {
+		"index" : 51,
+		"anim" : 18,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"luck" : {
+						"val" : 1,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"luck" : {
+						"val" : 1,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"luck" : {
+						"val" : 2,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"luck" : {
+						"val" : 2,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.misfortune": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"misfortune" : {
+		"index" : 52,
+		"anim" : 48,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"luck" : {
+						"val" : -1,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"luck" : {
+						"val" : -1,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"luck" : {
+						"val" : -2,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"luck" : {
+						"val" : -2,
+						"type" : "LUCK",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
 		},
-		"forceField"     :
-		{
-			"id": 12,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "0", "0", "0", "0" ]
-		},
-		"fireWall"       :
-		{
-			"id": 13,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"earthquake"     :
-		{
-			"id": 14,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"magicArrow"     :
-		{
-			"id": 15,
-			"effect": -1,
-			"anim": 64,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"iceBolt"        :
-		{
-			"id": 16,
-			"effect": -1,
-			"anim": 46,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"lightningBolt"  :
-		{
-			"id": 17,
-			"effect": -1,
-			"anim": 38,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"implosion"      :
-		{
-			"id": 18,
-			"effect": -1,
-			"anim": 10,
-			"ranges": [ "0", "0", "0", "0" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPIMP"
-			},
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"chainLightning" :
-		{
-			"id": 19,
-			"effect": -1,
-			"anim": 38,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage", "offensive"]
-		},
-		"frostRing"      :
-		{
-			"id": 20,
-			"effect": -1,
-			"anim": 45,
-			"ranges": [ "1", "1", "1", "1" ],
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"fireball"       :
-		{
-			"id": 21,
-			"effect": -1,
-			"anim": 53,
-			"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"inferno"        :
-		{
-			"id": 22,
-			"effect": -1,
-			"anim": 9,
-			"ranges": [ "0-2", "0-2", "0-2", "0-2" ],
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"meteorShower"   :
-		{
-			"id": 23,
-			"effect": -1,
-			"anim": 16,
-			"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPMET"
-			},
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"deathRipple"    :
-		{
-			"id": 24,
-			"effect": -1,
-			"anim": 8,
-			"ranges": [ "X", "X", "X", "X" ],
-			"flags" : ["damage", "offensive"],
-			"immunity":  ["SIEGE_WEAPON","UNDEAD","DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"destroyUndead"  :
-		{
-			"id": 25,
-			"effect": -1,
-			"anim": 29,
-			"ranges": [ "X", "X", "X", "X" ],
-			"flags" : ["damage", "offensive"],
-			"limit":["UNDEAD"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-
+		"counters" : {
+			"spell.fortune":true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"haste" : {
+		"index" : 53,
+		"anim" : 31,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 3,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 3,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 5,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : 5,
+						"type" : "STACKS_SPEED",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.slow": true
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"slow" : {
+		"index" : 54,
+		"anim" : 19,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : -25,
+						"type" : "STACKS_SPEED",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : -25,
+						"type" : "STACKS_SPEED",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : -50,
+						"type" : "STACKS_SPEED",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"val" : -50,
+						"type" : "STACKS_SPEED",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
 		},
-		"armageddon"     :
-		{
-			"id": 26,
-			"effect": -1,
-			"anim": 12,
-			"ranges": [ "X", "X", "X", "X" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPARM"
-			},
-			"flags" : ["damage", "offensive"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
-		},
-		"shield"         :
-		{
-			"id": 27,
-			"effect": 1,
-			"anim": 27,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
 
-				{
-					"type": 	"GENERAL_DAMAGE_REDUCTION",
-					"subtype":0,
-					"duration": "N_TURNS"
-				}
-			]
-		},
-		"airShield"      :
-		{
-			"id": 28,
-			"effect": 1,
-			"anim": 2,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"GENERAL_DAMAGE_REDUCTION",
-					"subtype":1,
-					"duration": "N_TURNS"
-				}
-			]
-		},
-		"fireShield"     :
-		{
-			"id": 29,
-			"effect": 1,
-			"anim": 11,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"FIRE_SHIELD",
-					"duration": "N_TURNS"
-				}
-			]
-		},
-		"protectAir"     :
-		{
-			"id": 30,
-			"effect": 1,
-			"anim": 22,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
+		"counters" : {
+			"spell.haste":true
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"slayer" : {
+		"index" : 55,
+		"anim" : 28,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"slayer" : {
+						"val" : 0,
+						"type" : "SLAYER",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"slayer" : {
+						"val" : 1,
+						"type" : "SLAYER",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"slayer" : {
+						"val" : 2,
+						"type" : "SLAYER",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"slayer" : {
+						"val" : 3,
+						"type" : "SLAYER",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"frenzy" : {
+		"index" : 56,
+		"anim" : 17,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"inFrenzy" : {
+						"val" : 100,
+						"type" : "IN_FRENZY",
+						"duration" : "STACK_GETS_TURN"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"inFrenzy" : {
+						"val" : 100,
+						"type" : "IN_FRENZY",
+						"duration" : "STACK_GETS_TURN"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"inFrenzy" : {
+						"val" : 150,
+						"type" : "IN_FRENZY",
+						"duration" : "STACK_GETS_TURN"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"inFrenzy" : {
+						"val" : 200,
+						"type" : "IN_FRENZY",
+						"duration" : "STACK_GETS_TURN"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"titanBolt" : {
+		"index" : 57,
+		"anim" : 38,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		}
+	},
+	"counterstrike" : {
+		"index" : 58,
+		"anim" : 7,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"additionalRetaliation" : {
+						"val" : 1,
+						"type" : "ADDITIONAL_RETALIATION",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"additionalRetaliation" : {
+						"val" : 1,
+						"type" : "ADDITIONAL_RETALIATION",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"additionalRetaliation" : {
+						"val" : 2,
+						"type" : "ADDITIONAL_RETALIATION",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"additionalRetaliation" : {
+						"val" : 2,
+						"type" : "ADDITIONAL_RETALIATION",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"berserk" : {
+		"index" : 59,
+		"anim" : 35,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 0,
+						"type" : "ATTACKS_NEAREST_CREATURE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 1,
+						"type" : "ATTACKS_NEAREST_CREATURE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0-1",
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 2,
+						"type" : "ATTACKS_NEAREST_CREATURE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0-2",
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 3,
+						"type" : "ATTACKS_NEAREST_CREATURE",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
 
-					"type": 	"SPELL_DAMAGE_REDUCTION",
-					"subtype":0,
-					"duration": "N_TURNS"
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"hypnotize" : {
+		"index" : 60,
+		"anim" : 21,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"hypnotized" : {
+						"val" : 0,
+						"type" : "HYPNOTIZED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"hypnotized" : {
+						"val" : 1,
+						"type" : "HYPNOTIZED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"hypnotized" : {
+						"val" : 2,
+						"type" : "HYPNOTIZED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"hypnotized" : {
+						"val" : 3,
+						"type" : "HYPNOTIZED",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
 
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"forgetfulness" : {
+		"index" : 61,
+                "targetType" : "CREATURE_EXPERT_MASSIVE", //fix dont remove
+		"anim" : 42,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"forgetfull" : {
+						"val" : 0,
+						"type" : "FORGETFULL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"forgetfull" : {
+						"val" : 1,
+						"type" : "FORGETFULL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"forgetfull" : {
+						"val" : 2,
+						"type" : "FORGETFULL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"forgetfull" : {
+						"val" : 3,
+						"type" : "FORGETFULL",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"limit" : {
+			"SHOOTER": true
+		},
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"blind" : {
+		"index" : 62,
+		"anim" : 6,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"generalAttackReduction" : {
+						"type" : "GENERAL_ATTACK_REDUCTION",
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"generalAttackReduction" : {
+						"type" : "GENERAL_ATTACK_REDUCTION",
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" :[
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"generalAttackReduction" : {
+						"type" : "GENERAL_ATTACK_REDUCTION",
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"generalAttackReduction" : {
+						"type" : "GENERAL_ATTACK_REDUCTION",
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
 				}
-			]
+			}
+		},
 
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"teleport" : {
+		"index" : 63,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"removeObstacle" : {
+		"index" : 64,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"clone" : {
+		"index" : 65,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"fireElemental" : {
+		"index" : 66,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"earthElemental" : {
+		"index" : 67,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"waterElemental" : {
+		"index" : 68,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"airElemental" : {
+		"index" : 69,
+		"anim" : -1,
+		"levels" : {
+			"none":{
+				"range" : "X"
+			},
+			"basic":{
+				"range" : "X"
+			},
+			"advanced":{
+				"range" : "X"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"stoneGaze" : {
+		"index" : 70,
+		"anim" : 70,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"poison" : {
+		"index" : 71,
+		"anim" : 67,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"poison" : {
+						"val" : 30,
+						"type" : "POISON",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					},
+					"stackHealth" : {
+						"val" : -10,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"poison" : {
+						"val" : 30,
+						"type" : "POISON",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					},
+					"stackHealth" : {
+						"val" : -10,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"poison" : {
+						"val" : 30,
+						"type" : "POISON",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					},
+					"stackHealth" : {
+						"val" : -10,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"poison" : {
+						"val" : 30,
+						"type" : "POISON",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					},
+					"stackHealth" : {
+						"val" : -10,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"bind" : {
+		"index" : 72,
+		"anim" : 68,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"bindEffect" : {
+						"val" : 0,
+						"type" : "BIND_EFFECT",
+						"duration" : "PERMANENT",
+						"addInfo" : -1
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"bindEffect" : {
+						"val" : 0,
+						"type" : "BIND_EFFECT",
+						"duration" : "PERMANENT",
+						"addInfo" : -1
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"bindEffect" : {
+						"val" : 0,
+						"type" : "BIND_EFFECT",
+						"duration" : "PERMANENT",
+						"addInfo" : -1
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"bindEffect" : {
+						"val" : 0,
+						"type" : "BIND_EFFECT",
+						"duration" : "PERMANENT",
+						"addInfo" : -1
+					}
+				}
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"disease" : {
+		"index" : 73,
+		"anim" : 69,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"attack" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"attack" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"attack" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"attack" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"paralyze" : {
+		"index" : 74,
+		"anim" : 70,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 74,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 74,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 74,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 74,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"age" : {
+		"index" : 75,
+		"anim" : 71,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"stackHealth" : {
+						"val" : -50,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"stackHealth" : {
+						"val" : -50,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"stackHealth" : {
+						"val" : -50,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"stackHealth" : {
+						"val" : -50,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"deathCloud" : {
+		"index" : 76,
+		"anim" : 72,
+		"levels" : {
+			"none":{
+				"range" : "0-1"
+			},
+			"basic":{
+				"range" : "0-1"
+			},
+			"advanced":{
+				"range" : "0-1"
+			},
+			"expert":{
+				"range" : "0-1"
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"thunderbolt" : {
+		"index" : 77,
+		"anim" : 38,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		}
+	},
+	"dispelHelpful" : {
+		"index" : 78,
+		"anim" : 41,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
 		},
-		"protectFire"    :
-		{
-			"id": 31,
-			"effect": 1,
-			"anim": 24,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"SPELL_DAMAGE_REDUCTION",
-					"subtype":1,
-					"duration": "N_TURNS"
-				}
-			]
-		},
-		"protectWater"   :
-		{
-			"id": 32,
-			"effect": 1,
-			"anim": 23,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"SPELL_DAMAGE_REDUCTION",
-					"subtype":2,
-					"duration": "N_TURNS"
-				}
-			]
-		},
-		"protectEarth"   :
-		{
-			"id": 33,
-			"effect": 1,
-			"anim": 26,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"SPELL_DAMAGE_REDUCTION",
-					"subtype":3,
-					"duration": "N_TURNS"
-				}
-			]
-		},
-		"antiMagic"      :
-		{
-			"id": 34,
-			"effect": 1,
-			"anim": 5,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"LEVEL_SPELL_IMMUNITY",
-					"valType":"INDEPENDENT_MAX",
-					"duration": "N_TURNS",
-					"values":[3,3,4,5]
-				}
-			]
-		},
-		"dispel"         :
-		{
-			"id": 35,
-			"effect": 0,
-			"anim": 41,
-			"ranges": [ "0", "0", "0", "X" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPDISP"
-			}
-		},
-		"magicMirror"    :
-		{
-			"id": 36,
-			"effect": 1,
-			"anim": 3,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"MAGIC_MIRROR",
-					"valType":"INDEPENDENT_MAX",
-					"duration": "N_TURNS"
-				}
-			]
-		},
-		"cure"           :
-		{
-			"id": 37,
-			"effect": 1,
-			"anim": 39,
-			"ranges": [ "0", "0", "0", "0" ]
-		},
-		"resurrection"   :
-		{
-			"id": 38,
-			"effect": 1,
-			"anim": 79,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["rising"],
-			"immunity":["UNDEAD", "NON_LIVING"]
-		},
-		"animateDead"    :
-		{
-			"id": 39,
-			"effect": 1,
-			"anim": 79,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["rising"],
-			"limit":["UNDEAD"]
-		},
-		"sacrifice"      :
-		{
-			"id": 40,
-			"effect": 1,
-			"anim": 79,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["rising"],
-			"immunity":["UNDEAD", "NON_LIVING"]
-		},
-		"bless"          :
-		{
-			"id": 41,
-			"effect": 1,
-			"anim": 36,
-			"ranges": [ "0", "0", "0", "X" ],
-			"counters" : [42],
-			"effects":
-			[
-				{
-					"type": 	"ALWAYS_MAXIMUM_DAMAGE",
-					"subtype": -1, //any attack
-					"valType":"INDEPENDENT_MAX",
-					"duration": "N_TURNS",
-					"values":[0, 0, 1, 1]
-				}
-			],
-			"immunity":["UNDEAD"]
-		},
-		"curse"          :
-		{
-			"id": 42,
-			"effect": -1,
-			"anim": 40,
-			"ranges": [ "0", "0", "0", "X" ], 
-			"counters" : [41],
-			"effects":
-			[
-				{
-					"type": 	"ALWAYS_MINIMUM_DAMAGE",
-					"subtype": -1, //any attack
-					"valType":	"INDEPENDENT_MAX",
-					"duration": "N_TURNS",
-					"values":[0, 0, 1, 1],
-					"ainfos":[0,0,20,20]
-				}
-			],
-			"immunity":["UNDEAD"]
-		},
-		"bloodlust"      :
-		{
-			"id": 43,
-			"effect": 1,
-			"anim": 4,
-			"ranges": [ "0", "0", "0", "X" ], 
-			"counters" : [45],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": "primSkill.attack",
-					"effectRange" : "ONLY_MELEE_FIGHT",
-					"duration": "N_TURNS",
-					"values":[3, 3, 6, 6]
-				}
-			]
-		},
-		"precision"      :
-		{
-			"id": 44,
-			"effect": 1,
-			"anim": 25,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": "primSkill.attack",
-					"effectRange" : "ONLY_DISTANCE_FIGHT",
-					"duration": "N_TURNS",
-					"values":[3, 3, 6, 6]
-				}
-			],
-			"limit":["SHOOTER"]
-		},
-		"weakness"       :
-		{
-			"id": 45,
-			"effect": -1,
-			"anim": 56,
-			"ranges": [ "0", "0", "0", "X" ], 
-			"counters" : [43],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": "primSkill.attack",
-					"duration": "N_TURNS",
-					"values":[-3,-3,-6,-6]
-				}
-			]
-		},
-		"stoneSkin"      :
-		{
-			"id": 46,
-			"effect": 1,
-			"anim": 54,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": "primSkill.defence",
-					"duration": "N_TURNS",
-					"values":[3, 3, 6, 6]
-				}
-			]
-		},
-		"disruptingRay"  :
-		{
-			"id": 47,
-			"effect": -1,
-			"anim": 14,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": "primSkill.defence",
-					"valueType": "ADDITIVE_VALUE",
-					"duration": "N_TURNS",
-					"values":[-3,-3,-4,-5]
-				}
-			]
-		},
-		"prayer"         :
-		{
-			"id": 48,
-			"effect": 1,
-			"anim": 0,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": "primSkill.attack",
-					"duration": "N_TURNS",
-					"values":[2, 2, 4, 4]
-				},
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": "primSkill.defence",
-					"duration": "N_TURNS",
-					"values":[2, 2, 4, 4]
-				},
-				{
-					"type": 	"STACKS_SPEED",
-					"duration": "N_TURNS",
-					"values":[2, 2, 4, 4],
-					"ainfos":[0,0,0,0]
-				}
-			]
-		},
-		"mirth"          :
-		{
-			"id": 49,
-			"effect": 1,
-			"anim": 20,
-			"ranges": [ "0", "0", "0", "X" ],
-			"counters" : [50],
-			"effects":
-			[
-				{
-					"type": 	"MORALE",
-					"duration": "N_TURNS",
-					"values":[1, 1, 2, 2]
-				}
-			]
-		},
-		"sorrow"         :
-		{
-			"id": 50,
-			"effect": -1,
-			"anim": 30,
-			"ranges": [ "0", "0", "0", "X" ], 
-			"counters" : [49],
-			"effects":
-			[
-				{
-					"type": 	"MORALE",
-					"duration": "N_TURNS",
-					"values":[-1,-1,-2,-2]
-				}
-			],
-			"immunity":["MIND_IMMUNITY", "UNDEAD", "NON_LIVING"]
-		},
-		"fortune"        :
-		{
-			"id": 51,
-			"effect": 1,
-			"anim": 18,
-			"ranges": [ "0", "0", "0", "X" ],
-			"counters" : [52],
-			"effects":
-			[
-				{
-					"type": 	"LUCK",
-					"duration": "N_TURNS",
-					"values":[1, 1, 2, 2]
-				}
-			]
-		},
-		"misfortune"     :
-		{
-			"id": 52,
-			"effect": -1,
-			"anim": 48,
-			"ranges": [ "0", "0", "0", "X" ],
-			"counters" : [51],
-			"effects":
-			[
-				{
-					"type": 	"LUCK",
-					"duration": "N_TURNS",
-					"values":[-1,-1,-2,-2]
-				}
-			]
-		},
-		"haste"          :
-		{
-			"id": 53,
-			"effect": 1,
-			"anim": 31,
-			"ranges": [ "0", "0", "0", "X" ],
-			"counters" : [54],
-			"effects":
-			[
-				{
-					"type": 	"STACKS_SPEED",
-					"duration": "N_TURNS",
-					"values":[3, 3, 5, 5],
-					"ainfos":[0,0,0,0]
-				}
-			],
-			"immunity":["SIEGE_WEAPON"]
-		},
-		"slow"           :
-		{
-			"id": 54,
-			"effect": -1,
-			"anim": 19,
-			"ranges": [ "0", "0", "0", "X" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPSLOW"
-			},
-			"counters" : [53],
-			"effects":
-			[
-				{
-					"type": 	"STACKS_SPEED",
-					"valueType": "PERCENT_TO_ALL",
-					"duration": "N_TURNS",
-					"values":[-25,-25,-50,-50],
-					"ainfos":[0,0,0,0]
-				}
-			],
-			"immunity":["SIEGE_WEAPON"]
-		},
-		"slayer"         :
-		{
-			"id": 55,
-			"effect": 1,
-			"anim": 28,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"SLAYER",
-					"duration": "N_TURNS",
-					"values":[0,1,2,3]
-				}
-			]
-		},
-		"frenzy"         :
-		{
-			"id": 56,
-			"effect": 1,
-			"anim": 17,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"IN_FRENZY",
-					"duration": "STACK_GETS_TURN",
-					"values":[100, 100, 150, 200]
-				}
-			]
-		},
-		"titanBolt"      :
-		{
-			"id": 57,
-			"effect": -1,
-			"anim": 38,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage", "offensive"]
-		},
-		"counterstrike"  :
-		{
-			"id": 58,
-			"effect": 1,
-			"anim": 7,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"ADDITIONAL_RETALIATION",
-					"duration": "N_TURNS",
-					"values":[1, 1, 2, 2]
-				}
-			]
-		},
-		"berserk"        :
-		{
-			"id": 59,
-			"effect": -1,
-			"anim": 35,
-			"ranges": [ "0", "0", "0-1", "0-2" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPBERS"
-			},
-			"effects":
-			[
-				{
-					"type": 	"ATTACKS_NEAREST_CREATURE",
-					"duration": "N_TURNS",
-					"values":[0,1,2,3]
-				}
-			],
-			"immunity":["MIND_IMMUNITY", "UNDEAD", "NON_LIVING"]
-		},
-		"hypnotize"      :
-		{
-			"id": 60,
-			"effect": -1,
-			"anim": 21,
-			"ranges": [ "0", "0", "0", "0" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPHYPN"
-			},
-			"effects":
-			[
-				{
-					"type": 	"HYPNOTIZED",
-					"duration": "N_TURNS",
-					"values":[0,1,2,3]
-				}
-			],
-			"immunity":["MIND_IMMUNITY", "UNDEAD", "NON_LIVING"]
-		},
-		"forgetfulness"  :
-		{
-			"id": 61,
-			"effect": -1,
-			"anim": 42,
-			"ranges": [ "0", "0", "0", "X" ],
-			"effects":
-			[
-				{
-					"type": 	"FORGETFULL",
-					"duration": "N_TURNS",
-					"values":[0,1,2,3]
-				}
-			],
-			"limit":["SHOOTER"],
-			"immunity":["MIND_IMMUNITY", "UNDEAD", "NON_LIVING"]
-		},
-		"blind"          :
-		{
-			"id": 62,
-			"effect": -1,
-			"anim": 6,
-			"ranges": [ "0", "0", "0", "0" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPBLIND"
-			},
-			"effects":
-			[
-				{
-					"type": 	"NOT_ACTIVE",
-					"subtype": 	62,
-					"duration":	["UNITL_BEING_ATTACKED","N_TURNS"],
-					"values":[0,0,0,0]
-				},
-				{
-					"type": 	"GENERAL_ATTACK_REDUCTION",
-					"duration":	["UNITL_BEING_ATTACKED","N_TURNS"]
-				},
-				{
-					"type": 	"NO_RETALIATION",
-					"duration":	"UNITL_BEING_ATTACKED",
-					"values":[0,0,0,0]
-				}
-			],
-			"immunity":["MIND_IMMUNITY", "UNDEAD", "NON_LIVING"]
-		},
-		"teleport"       :
-		{
-			"id": 63,
-			"effect": 1,
-			"anim": -1,
-			"ranges": [ "0", "0", "0", "0" ],
-			"immunity":["SIEGE_WEAPON"]
-		},
-		"removeObstacle" :
-		{
-			"id": 64,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"clone"          :
-		{
-			"id": 65,
-			"effect": 1,
-			"anim": -1,
-			"ranges": [ "0", "0", "0", "0" ],
-			"immunity":["SIEGE_WEAPON"]
-		},
-		"fireElemental"  :
-		{
-			"id": 66,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"earthElemental" :
-		{
-			"id": 67,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"waterElemental" :
-		{
-			"id": 68,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"airElemental"   :
-		{
-			"id": 69,
-			"effect": 0,
-			"anim": -1,
-			"ranges": [ "X", "X", "X", "X" ]
-		},
-		"stoneGaze"      :
-		{
-			"id": 70,
-			"effect": 0,
-			"anim": 70,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"NOT_ACTIVE",
-					"subtype": 	62,
-					"duration":	["UNITL_BEING_ATTACKED","N_TURNS"],
-					"values":[0,0,0,0]
-				},
-				{
-					"type": 	"NO_RETALIATION",
-					"duration":	"UNITL_BEING_ATTACKED",
-					"values":[0,0,0,0]
-				}
-			]
-		},
-		"poison"         :
-		{
-			"id": 71,
-			"effect": -1,
-			"anim": 67,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"POISON",
-					"valueType": 	"INDEPENDENT_MAX",
-					"duration": "N_TURNS",
-					"values":[30,30,30,30]
-				},
-				{
-					"type": 	"STACK_HEALTH",
-					"val" : 	-10,
-					"valueType": 	"PERCENT_TO_ALL",
-					"duration": "N_TURNS",
-					"values":[-10,-10,-10,-10]
-				}
-			],
-			"immunity":["UNDEAD", "NON_LIVING"]
-		},
-		"bind"           :
-		{
-			"id": 72,
-			"effect": 0,
-			"anim": 68,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"BIND_EFFECT",
-					"duration" : 	"PERMANENT",
-					"addInfo" : 	-1,
-					"values":[0,0,0,0]
-				}
-			]
-		},
-		"disease"        :
-		{
-			"id": 73,
-			"effect": -1,
-			"anim": 69,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": 	"primSkill.attack",
-					"duration": "N_TURNS",
-					"values":[-2,-2,-2,-2]
-				},
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": 	"primSkill.defence",
-					"duration": "N_TURNS",
-					"values":[-2,-2,-2,-2]
-				}
-			],
-			"immunity":["UNDEAD", "NON_LIVING"]
-		},
-		"paralyze"       :
-		{
-			"id": 74,
-			"effect": -1,
-			"anim": 70,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"NOT_ACTIVE",
-					"subtype": 	74,
-					"duration":	["UNITL_BEING_ATTACKED","N_TURNS"],
-					"values":[0,0,0,0]
-				},
-				{
-					"type": 	"NO_RETALIATION",
-					"duration":	"UNITL_BEING_ATTACKED",
-					"values":[0,0,0,0]
-				}
-			]
-		},
-		"age"            :
-		{
-			"id": 75,
-			"effect": -1,
-			"anim": 71,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"STACK_HEALTH",
-					"valueType": 	"PERCENT_TO_ALL",
-					"duration": "N_TURNS",
-					"values":[-50,-50,-50,-50]
-				}
-			],
-			"immunity":["UNDEAD", "NON_LIVING"]
-		},
-		"deathCloud"     :
-		{
-			"id": 76,
-			"effect": 0,
-			"anim": 72,
-			"ranges": [ "0-1", "0-1", "0-1", "0-1" ],
-			"immunity":["UNDEAD", "NON_LIVING"]
-		},
-		"thunderbolt"    :
-		{
-			"id": 77,
-			"effect": -1,
-			"anim": 38,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage", "offensive"]
-		},
-		"dispelHelpful"  :
-		{
-			"id": 78,
-			"effect": -1,
-			"anim": 41,
-			"ranges": [ "0", "0", "0", "0" ],
-			"graphics":{
-				"iconImmune":"ZVS/LIB1.RES/E_SPDISB"
-			}
-		},
-		"deathStare"     :
-		{
-			"id": 79,
-			"effect": 0,
-			"anim": 80,
-			"ranges": [ "0", "0", "0", "0" ],
-			"immunity":["UNDEAD", "NON_LIVING"]
-		},
-		"acidBreath"     :
-		{
-			"id": 80,
-			"effect": 0,
-			"anim": 81,
-			"ranges": [ "0", "0", "0", "0" ],
-			"effects":
-			[
-				{
-					"type": 	"PRIMARY_SKILL",
-					"subtype": 	"primSkill.attack",
-					"duration" : 	"PERMANENT",
-					"valueType": 	"ADDITIVE_VALUE",
-					"values":[-3,-3,-3,-3]
-				}
-			]
-		},
-		"acidBreathDamage"     :
-		{
-			"id": 81,
-			"effect": 0,
-			"anim": 81,
-			"ranges": [ "0", "0", "0", "0" ],
-			"flags" : ["damage"],
-			"immunity" : ["DIRECT_DAMAGE_IMMUNITY"]
 
+		"flags" : {
+			"negative": true
+		}
+	},
+	"deathStare" : {
+		"index" : 79,
+		"anim" : 80,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"acidBreath" : {
+		"index" : 80,
+		"anim" : 81,
+		"levels" : {
+			"none":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "PERMANENT",
+						"valueType" : "ADDITIVE_VALUE"
+					}
+				}
+			},
+			"basic":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "PERMANENT",
+						"valueType" : "ADDITIVE_VALUE"
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "PERMANENT",
+						"valueType" : "ADDITIVE_VALUE"
+					}
+				}
+			},
+			"expert":{
+				"range" : "0",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "PERMANENT",
+						"valueType" : "ADDITIVE_VALUE"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"acidBreathDamage" : {
+		"index" : 81,
+		"anim" : 81,
+		"levels" : {
+			"none":{
+				"range" : "0"
+			},
+			"basic":{
+				"range" : "0"
+			},
+			"advanced":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"indifferent": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
 		}
 	}
 }

+ 3 - 3
lib/BattleState.cpp

@@ -166,9 +166,9 @@ ui32 CBattleInfoCallback::calculateHealedHP(const CGHeroInstance * caster, const
 	bool resurrect = spell->isRisingSpell();
 	int healedHealth;
 	if (spell->id == SpellID::SACRIFICE && sacrificedStack)
-		healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count;
+		healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->getPower(caster->getSpellSchoolLevel(spell))) * sacrificedStack->count;
 	else
-		healedHealth = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)];
+		healedHealth = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) * spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)); //???
 	healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack);
 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
 }
@@ -182,7 +182,7 @@ ui32 CBattleInfoCallback::calculateHealedHP(int healedHealth, const CSpell * spe
 ui32 CBattleInfoCallback::calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const
 {
 	bool resurrect = spell->isRisingSpell();
-	int healedHealth = usedSpellPower * spell->power + spell->powers[spellSchoolLevel];
+	int healedHealth = usedSpellPower * spell->power + spell->getPower(spellSchoolLevel);
 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
 }
 bool BattleInfo::resurrects(SpellID spellid) const

+ 14 - 14
lib/CBattleCallback.cpp

@@ -808,8 +808,8 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
 	auto battleBonusValue = [&](const IBonusBearer * bearer, CSelector selector) -> int
 	{
 		auto noLimit = Selector::effectRange(Bonus::NO_LIMIT);
-		auto limitMatches = info.shooting 
-				? Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT) 
+		auto limitMatches = info.shooting
+				? Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)
 				: Selector::effectRange(Bonus::ONLY_MELEE_FIGHT);
 
 		//any regular bonuses or just ones for melee/ranged
@@ -872,7 +872,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
 		{
 			if(defenderType->idNumber == affectedId)
 			{
-				attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->powers[spLevel];
+				attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->getPower(spLevel);
 				break;
 			}
 		}
@@ -1573,7 +1573,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
 			{
 				//can't clone already cloned creature
 				if (vstd::contains(subject->state, EBattleStackState::CLONED))
-					return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; 
+					return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
 				//TODO: how about stacks casting Clone?
 				//currently Clone casted by stack is assumed Expert level
 				ui8 schoolLevel;
@@ -1585,14 +1585,14 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
 				{
 					schoolLevel = 3;
 				}
-				
+
 				if (schoolLevel < 3)
 				{
 					int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
 					int creLevel = subject->getCreature()->level;
 					if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
-						return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;	
-				}					
+						return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+				}
 			}
 			break;
 		case SpellID::DISPEL_HELPFUL_SPELLS:
@@ -1630,7 +1630,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
 			ui64 subjectHealth = (subject->count - 1) * subject->MaxHealth() + subject->firstHPleft;
 			//apply 'damage' bonus for hypnotize, including hero specialty
 			ui64 maxHealth = calculateSpellBonus (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
-				* spell->power + spell->powers[caster->getSpellSchoolLevel(spell)], spell, caster, subject);
+				* spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)), spell, caster, subject);
 			if (subjectHealth > maxHealth)
 				return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
 		}
@@ -1689,7 +1689,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 		bool allStacksImmune = true;
 		//we are interested only in enemy stacks when casting offensive spells
 		auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
-		for(auto stack : stacks) 
+		for(auto stack : stacks)
 		{
 			if(!battleIsImmune(castingHero, spell, mode, stack->position))
 			{
@@ -1866,19 +1866,19 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 				switch (obstacle->obstacleType)
 				{
 				case CObstacleInstance::ABSOLUTE_OBSTACLE: //cliff-like obstacles cant be removed
-				case CObstacleInstance::MOAT: 
+				case CObstacleInstance::MOAT:
 					return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 				case CObstacleInstance::USUAL:
 					return ESpellCastProblem::OK;
 
 // 				//TODO FIRE_WALL only for ADVANCED level casters
 // 				case CObstacleInstance::FIRE_WALL:
-// 					return 
+// 					return
 // 				//TODO other magic obstacles for EXPERT
 // 				case CObstacleInstance::QUICKSAND:
 // 				case CObstacleInstance::LAND_MINE:
 // 				case CObstacleInstance::FORCE_FIELD:
-// 					return 
+// 					return
 				default:
 //					assert(0);
 					return ESpellCastProblem::OK;
@@ -1959,7 +1959,7 @@ ui32 CBattleInfoCallback::calculateSpellDmg( const CSpell * sp, const CGHeroInst
 		return 0;
 
 	ret = usedSpellPower * sp->power;
-	ret += sp->powers[spellSchoolLevel];
+	ret += sp->getPower(spellSchoolLevel);
 
 	//affected creature-specific part
 	if(affectedCreature)
@@ -2156,7 +2156,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
 	RETURN_IF_NOT_BATTLE(SpellID::NONE);
 	std::vector<SpellID> possibleSpells;
 
-	for(const CSpell *spell : VLC->spellh->spells)
+	for(const CSpell *spell : VLC->spellh->objects)
 	{
 		if (spell->isPositive()) //only positive
 		{

+ 16 - 16
lib/CBonusTypeHandler.h

@@ -8,7 +8,7 @@
  *
  */
 
-#pragma once 
+#pragma once
 
 #include "IBonusTypeHandler.h"
 #include "IHandlerBase.h"
@@ -29,15 +29,15 @@ class MacroString
 		};
 		Item(ItemType _type, std::string _value): type(_type), value(_value){};
 		ItemType type;
-		std::string value; //consant string or macro name
+		std::string value; //constant string or macro name
 	};
 	std::vector<Item> items;
 public:
 	typedef std::function <std::string(const std::string&)> GetValue;
-	
+
 	MacroString(){};
 	MacroString(const std::string &format);
-	
+
 	std::string build(const GetValue& getValue) const;
 };
 
@@ -46,49 +46,49 @@ class DLL_LINKAGE CBonusType
 public:
 	CBonusType();
 	~CBonusType();
-	
+
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & icon & nameTemplate & descriptionTemplate & hidden;
-		
+
 		if (!h.saving)
 			buildMacros();
 	}
 protected:
-	
+
 private:
 	void buildMacros();
 	MacroString name, description;
-	
+
 	friend class CBonusTypeHandler;
 	std::string icon;
 	std::string nameTemplate, descriptionTemplate;
-	
+
 	bool hidden;
 };
 
- 
+
 class DLL_LINKAGE CBonusTypeHandler : public IBonusTypeHandler
 {
 public:
 	CBonusTypeHandler();
 	virtual ~CBonusTypeHandler();
-	
+
 	std::string bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const override;
 	std::string bonusToGraphics(const Bonus *bonus) const override;
-	
+
 	void load();
 	void load(const JsonNode& config);
-	
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & bonusTypes;
 	}
 private:
-	
+
 	void loadItem(const JsonNode &source, CBonusType &dest);
-	
-	std::vector<CBonusType> bonusTypes; //index = BonusTypeID 
+
+	std::vector<CBonusType> bonusTypes; //index = BonusTypeID
 
 };

+ 20 - 16
lib/CGameState.cpp

@@ -82,7 +82,7 @@ static CApplier<CBaseForGSApply> *applierGs = nullptr;
 // 	virtual void preInit()=0;
 // 	virtual void postInit()=0;
 // };
-// 
+//
 // template <typename T>
 // class CObjectCaller : public IObjectCaller
 // {
@@ -101,29 +101,29 @@ static CApplier<CBaseForGSApply> *applierGs = nullptr;
 // {
 // public:
 // 	std::vector<IObjectCaller*> apps;
-// 
+//
 // 	template<typename T> void registerType(const T * t=nullptr)
 // 	{
 // 		apps.push_back(new CObjectCaller<T>);
 // 	}
-// 
+//
 // 	CObjectCallersHandler()
 // 	{
 // 		registerTypesMapObjects(*this);
 // 	}
-// 
+//
 // 	~CObjectCallersHandler()
 // 	{
 // 		for (auto & elem : apps)
 // 			delete elem;
 // 	}
-// 
+//
 // 	void preInit()
 // 	{
 // // 		for (size_t i = 0; i < apps.size(); i++)
 // // 			apps[i]->preInit();
 // 	}
-// 
+//
 // 	void postInit()
 // 	{
 // 	//for (size_t i = 0; i < apps.size(); i++)
@@ -1319,7 +1319,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 		}
 	}
 
-	if(!(travelOptions.whatHeroKeeps & 8)) 
+	if(!(travelOptions.whatHeroKeeps & 8))
 	{
 		for(CGHeroInstance * cgh : crossoverHeroes)
 		{
@@ -1344,18 +1344,18 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 				if(artifactPosition == ArtifactPosition::SPELLBOOK) continue; // do not handle spellbook this way
 
 				const ArtSlotInfo *info = hero->getSlot(artifactPosition);
-				if(!info) 
+				if(!info)
 					continue;
 
 				// TODO: why would there be nullptr artifacts?
 				const CArtifactInstance *art = info->artifact;
-				if(!art) 
+				if(!art)
 					continue;
 
 				int id  = art->artType->id;
 				assert( 8*18 > id );//number of arts that fits into h3m format
 				bool takeable = travelOptions.artifsKeptByHero[id / 8] & ( 1 << (id%8) );
-				
+
 				ArtifactLocation al(hero, artifactPosition);
 				if(!takeable  &&  !al.getSlot()->locked)  //don't try removing locked artifacts -> it crashes #1719
 					al.removeArtifact();
@@ -1817,7 +1817,8 @@ void CGameState::initTowns()
 						ev.buildings.insert(BuildingID::HORDE_2);
 				}
 		}
-		//init spells
+		//init spells
+		logGlobal->debugStream() << "\t\tTown init spells";
 		vti->spells.resize(GameConstants::SPELL_LEVELS);
 
 		for(ui32 z=0; z<vti->obligatorySpells.size();z++)
@@ -1825,14 +1826,15 @@ void CGameState::initTowns()
 			CSpell *s = vti->obligatorySpells[z].toSpell();
 			vti->spells[s->level-1].push_back(s->id);
 			vti->possibleSpells -= s->id;
-		}
+		}
+		logGlobal->debugStream() << "\t\tTown init spells2";
 		while(vti->possibleSpells.size())
 		{
 			ui32 total=0;
 			int sel = -1;
 
 			for(ui32 ps=0;ps<vti->possibleSpells.size();ps++)
-				total += vti->possibleSpells[ps].toSpell()->probabilities[vti->subID];
+				total += vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
 
 			if (total == 0) // remaining spells have 0 probability
 				break;
@@ -1840,7 +1842,7 @@ void CGameState::initTowns()
 			int r = ran()%total;
 			for(ui32 ps=0; ps<vti->possibleSpells.size();ps++)
 			{
-				r -= vti->possibleSpells[ps].toSpell()->probabilities[vti->subID];
+				r -= vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
 				if(r<0)
 				{
 					sel = ps;
@@ -1856,7 +1858,9 @@ void CGameState::initTowns()
 		}
 		vti->possibleSpells.clear();
 		if(vti->getOwner() != PlayerColor::NEUTRAL)
-			getPlayer(vti->getOwner())->towns.push_back(vti);
+			getPlayer(vti->getOwner())->towns.push_back(vti);
+        logGlobal->debugStream() << "\t\tTown init spells3";
+
 	}
 }
 
@@ -3259,7 +3263,7 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname)
 			const JsonNode & spells = n["spells"];
 			if(spells.getType() == JsonNode::DATA_STRING  &&  spells.String() == "all")
 			{
-				for(auto spell : VLC->spellh->spells)
+				for(auto spell : VLC->spellh->objects)
 					if(spell->id <= SpellID::SUMMON_AIR_ELEMENTAL)
 						ss.spells.insert(spell->id);
 			}

+ 4 - 2
lib/CModHandler.cpp

@@ -14,6 +14,7 @@
 #include "StringConstants.h"
 #include "CStopWatch.h"
 #include "IHandlerBase.h"
+#include "CSpellHandler.h"
 
 /*
  * CModHandler.cpp, part of VCMI engine
@@ -337,13 +338,14 @@ void CContentHandler::ContentTypeHandler::afterLoadFinalization()
 
 CContentHandler::CContentHandler()
 {
-	handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
+ 	handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
 	handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact")));
 	handlers.insert(std::make_pair("creatures", ContentTypeHandler(VLC->creh, "creature")));
 	handlers.insert(std::make_pair("factions", ContentTypeHandler(VLC->townh, "faction")));
 	handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
+    handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
 
-	//TODO: spells, bonuses, something else?
+	//TODO: bonuses, something else?
 }
 
 bool CContentHandler::preloadModData(std::string modName, JsonNode modConfig, bool validate)

+ 2 - 2
lib/CObjectHandler.cpp

@@ -1521,7 +1521,7 @@ void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
 
 int CGHeroInstance::getSpellCost(const CSpell *sp) const
 {
-	return sp->costs[getSpellSchoolLevel(sp)];
+	return sp->getCost(getSpellSchoolLevel(sp));
 }
 
 void CGHeroInstance::pushPrimSkill( PrimarySkill::PrimarySkill which, int val )
@@ -5538,7 +5538,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
 			iw.text.addTxt(MetaString::ADVOB_TXT, 184); //%s learns a spell
 		}
 		iw.text.addReplacement(h->name);
-		std::vector<ConstTransitivePtr<CSpell> > * sp = &VLC->spellh->spells;
+		std::vector<ConstTransitivePtr<CSpell> > * sp = &VLC->spellh->objects;
 		for(auto i=spells.cbegin(); i != spells.cend(); i++)
 		{
 			if ((*sp)[*i]->level <= h->getSecSkillLevel(SecondarySkill::WISDOM) + 2) //enough wisdom

+ 493 - 149
lib/CSpellHandler.cpp

@@ -3,11 +3,12 @@
 
 #include "CGeneralTextHandler.h"
 #include "filesystem/Filesystem.h"
-#include "VCMI_Lib.h"
+
 #include "JsonNode.h"
 #include <cctype>
 #include "BattleHex.h"
 #include "CModHandler.h"
+#include "StringConstants.h"
 
 /*
  * CSpellHandler.cpp, part of VCMI engine
@@ -18,6 +19,12 @@
  * Full text of license available in license.txt file, in main folder
  *
  */
+
+namespace SpellConfigJson
+{
+    static const std::string level_names[] = {"none","basic","advanced","expert"};
+}
+
 using namespace boost::assign;
 
 namespace SRSLPraserHelpers
@@ -124,11 +131,18 @@ namespace SRSLPraserHelpers
 
 using namespace SRSLPraserHelpers;
 
-CSpell::CSpell()
+CSpell::CSpell():
+    id(SpellID::NONE), level(0),
+    earth(false),water(false),fire(false),air(false),
+    power(0),
+    combatSpell(false),creatureAbility(false),
+    positiveness(ESpellPositiveness::NEUTRAL),
+    mainEffectAnim(-1),
+    defaultProbability(0),
+    isRising(false),isDamage(false),isOffensive(false),targetType(ETargetType::NO_TARGET)
+
 {
-	isDamage = false;
-	isRising = false;
-	isOffensive = false;
+
 }
 
 CSpell::~CSpell()
@@ -263,11 +277,16 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
 		logGlobal->errorStream() << __FUNCTION__ << " This spell ("  + name + ") is missing entry for level " << level;
 		return;
 	}
+	if (effects[level].empty())
+    {
+		logGlobal->errorStream() << __FUNCTION__ << " This spell ("  + name + ") has no effects for level " << level;
+		return;
+    }
+
 	lst.reserve(lst.size() + effects[level].size());
 
 	for (Bonus *b : effects[level])
 	{
-		//TODO: value, add value
 		lst.push_back(Bonus(*b));
 	}
 }
@@ -275,15 +294,26 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
 bool CSpell::isImmuneBy(const IBonusBearer* obj) const
 {
 	//todo: use new bonus API
+	//1. Check limiters
 	for(auto b : limiters)
 	{
 		if (!obj->hasBonusOfType(b))
 			return true;
 	}
 
+	//2. Check absolute immunities
+	//todo: check config: some creatures are unaffected always, for example undead to resurrection.
+    for(auto b : absoluteImmunities)
+	{
+		if (obj->hasBonusOfType(b))
+			return true;
+	}
+
+    //3. Check negation
 	if (obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES)) //Orb of vulnerability
-		return false; //TODO: some creaures are unaffected always, for example undead to resurrection.
+		return false;
 
+    //4. Check negatable immunities
 	for(auto b : immunities)
 	{
 		if (obj->hasBonusOfType(b))
@@ -302,6 +332,7 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
 		return false;
 	};
 
+    //4. Check elemental immunities
 	if (fire)
 	{
 		if (battleTestElementalImmunity(Bonus::FIRE_IMMUNITY))
@@ -349,6 +380,28 @@ void CSpell::setAttributes(const std::string& newValue)
 		targetType = NO_TARGET;
 }
 
+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;
+    }
+}
+
+
 
 bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
 {
@@ -359,68 +412,108 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
 		return false;
 }
 
-CSpell * CSpellHandler::loadSpell(CLegacyConfigParser & parser, const SpellID id)
+CSpellHandler::CSpellHandler()
 {
-	auto  spell = new CSpell; //new currently being read spell
 
-	spell->id      = id;
+}
 
-	spell->name    = parser.readString();
-	spell->abbName = parser.readString();
-	spell->level   = parser.readNumber();
-	spell->earth   = parser.readString() == "x";
-	spell->water   = parser.readString() == "x";
-	spell->fire    = parser.readString() == "x";
-	spell->air     = parser.readString() == "x";
+std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
+{
+    using namespace SpellConfigJson;
+    std::vector<JsonNode> legacyData;
 
-	spell->costs = parser.readNumArray<si32>(4);
+	CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
 
-	spell->power = parser.readNumber();
-	spell->powers = parser.readNumArray<si32>(4);
+	auto readSchool = [&](JsonMap& schools, const std::string& name)
+	{
+        if (parser.readString() == "x")
+        {
+            schools[name].Bool() = true;
+        }
+	};
 
-	for (int i = 0; i < 9 ; i++)
-		spell->probabilities[i] = parser.readNumber();
+	auto read = [&,this](bool combat, bool ability)
+	{
+		do
+		{
+		    JsonNode lineNode(JsonNode::DATA_STRUCT);
 
-	spell->AIVals = parser.readNumArray<si32>(4);
+		    const si32 id = legacyData.size();
 
-	for (int i = 0; i < 4 ; i++)
-		spell->descriptions.push_back(parser.readString());
+            lineNode["index"].Float() = id;
+            lineNode["type"].String() = ability ? "ability" : (combat ? "combat" : "adventure");
 
-	std::string attributes = parser.readString();
+            lineNode["name"].String() = parser.readString();
 
+            parser.readString(); //ignored unused abbreviated name
+            lineNode["level"].Float()      = parser.readNumber();
 
-	//spell fixes
-	if (id == SpellID::FORGETFULNESS)
-	{
-		//forgetfulness needs to get targets automatically on expert level
-		boost::replace_first(attributes, "CREATURE_TARGET", "CREATURE_TARGET_2");
-	}
+            auto& schools = lineNode["school"].Struct();
 
-	if (id == SpellID::DISRUPTING_RAY)
-	{
-		// disrupting ray will now affect single creature
-		boost::replace_first(attributes,"2", "");
-	}
+            readSchool(schools, "earth");
+            readSchool(schools, "water");
+            readSchool(schools, "fire");
+            readSchool(schools, "air");
 
+            auto& levels = lineNode["levels"].Struct();
 
-	spell->setAttributes(attributes);
-	spell->mainEffectAnim = -1;
-	return spell;
-}
 
-CSpellHandler::CSpellHandler()
-{
-	CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
 
-	auto read = [&,this](bool combat, bool alility)
-	{
-		do
-		{
-			const SpellID id = SpellID(spells.size());
-			CSpell * spell = loadSpell(parser,id);
-			spell->combatSpell = combat;
-			spell->creatureAbility = alility;
-			spells.push_back(spell);
+            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());
+
+            std::string attributes = parser.readString();
+
+            std::string targetType = "NO_TARGET";
+
+            if(attributes.find("CREATURE_TARGET_1") != std::string::npos
+                || attributes.find("CREATURE_TARGET_2") != std::string::npos)
+                targetType = "CREATURE_EXPERT_MASSIVE";
+            else if(attributes.find("CREATURE_TARGET") != std::string::npos)
+                targetType = "CREATURE";
+            else if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
+                targetType = "OBSTACLE";
+
+            lineNode["targetType"].String() = targetType;
+
+
+
+            //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];
+
+            }
+
+
+//            logGlobal->errorStream() << lineNode;
+		    legacyData.push_back(lineNode);
+
+
 		}
 		while (parser.endLine() && !parser.isNextEntryEmpty());
 	};
@@ -438,128 +531,379 @@ CSpellHandler::CSpellHandler()
 	skip(3);
 	read(true,true);//read creature abilities
 
-	spells.push_back(spells[SpellID::ACID_BREATH_DEFENSE]); //clone Acid Breath attributes for Acid Breath damage effect
+    //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);
 
-	//loading of additional spell traits
-	JsonNode config(ResourceID("config/spell_info.json"));
-	config.setMeta("core");
+    objects.resize(legacyData.size());
 
-	for(auto &spell : config["spells"].Struct())
-	{
-		//reading exact info
-		int spellID = spell.second["id"].Float();
-		CSpell *s = spells[spellID];
+	return legacyData;
+}
 
-		s->positiveness = spell.second["effect"].Float();
-		s->mainEffectAnim = spell.second["anim"].Float();
+const std::string CSpellHandler::getTypeName()
+{
+    return "spell";
+}
 
-		s->range.resize(4);
-		int idx = 0;
-		for(const JsonNode &range : spell.second["ranges"].Vector())
-			s->range[idx++] = range.String();
+static void fatalConfigurationError()
+{
+   throw std::runtime_error("SpellHandler: Fatal configuration error, See log for details");
+}
 
-		s->counteredSpells = spell.second["counters"].convertTo<std::vector<SpellID> >();
+CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
+{
+    using namespace SpellConfigJson;
 
-		s->identifier = spell.first;
-		VLC->modh->identifiers.registerObject("core", "spell", spell.first, spellID);
+    CSpell * spell = new CSpell();
 
-		const JsonNode & flags_node = spell.second["flags"];
-		if (!flags_node.isNull())
-		{
-			auto flags = flags_node.convertTo<std::vector<std::string> >();
+    const auto type_str = json["type"].String();
 
-			for (const auto & flag : flags)
-			{
-				if (flag == "damage")
-				{
-					s->isDamage = true;
-				}
-				else if (flag == "rising")
-				{
-					s->isRising = true;
-				}
-				else if (flag == "offensive")
-				{
-					s->isOffensive = true;
-				}
-			}
-		}
+    if (type_str == "ability")
+    {
+        spell->creatureAbility = true;
+        spell->combatSpell = true;
+    }
+    else
+    {
+       spell->creatureAbility = false;
+       spell->combatSpell = type_str == "combat";
+    }
 
-		const JsonNode & effects_node = spell.second["effects"];
+    spell->name = json["name"].String();
 
-		for (const JsonNode & bonus_node : effects_node.Vector())
-		{
-			auto &v_node = bonus_node["values"];
-			auto &a_node = bonus_node["ainfos"];
+    logGlobal->traceStream() << __FUNCTION__ << ": loading spell " << spell->name;
 
-			auto v = v_node.convertTo<std::vector<int> >();
-			auto a = a_node.convertTo<std::vector<int> >();
+    auto readFlag = [](const JsonNode& flagsNode, const std::string& name)
+    {
+        if (flagsNode.getType() != JsonNode::DATA_STRUCT)
+        {
+            logGlobal->errorStream() << "Flags node shall be object";
+            return false;
+        }
 
-			if(v.size() && v.size() != GameConstants::SPELL_SCHOOL_LEVELS)
-				logGlobal->errorStream() << s->name << " should either have no values or exactly " << GameConstants::SPELL_SCHOOL_LEVELS;
-			if(a.size() && a.size() != GameConstants::SPELL_SCHOOL_LEVELS)
-				logGlobal->errorStream() << s->name << " should either have no ainfos or exactly " << GameConstants::SPELL_SCHOOL_LEVELS;
-			
-			s->effects.resize(GameConstants::SPELL_SCHOOL_LEVELS);
+        const JsonNode& flag = flagsNode[name];
 
-			for (int i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
-			{
-				Bonus * b = JsonUtils::parseBonus(bonus_node);
-				b->sid = s->id; //for all
-				b->source = Bonus::SPELL_EFFECT;//for all
-				b->val = s->powers[i];
+        if (flag.isNull())
+        {
+            return false;
+        }
+        else if (flag.getType() == JsonNode::DATA_BOOL)
+        {
+            return flag.Bool();
+        }
+        else
+        {
+            logGlobal->errorStream() << "Flag shall be boolean: "<<name;
+            return false;
+        }
 
-				if (!v.empty())
-					b->val = v[i];
-				if (!a.empty())
-					b->additionalInfo = a[i];
+    };
 
-				s->effects[i].push_back(b);
-			}
-		}
+    const auto school_names = json["school"];
 
-		auto find_in_map = [](std::string name, std::vector<Bonus::BonusType> &vec)
-		{
-			auto it = bonusNameMap.find(name);
-			if (it == bonusNameMap.end())
-			{
-                logGlobal->errorStream() << "Error: invalid bonus name" << name;
-			}
-			else
-			{
-				vec.push_back((Bonus::BonusType)it->second);
-			}
-		};
+    spell->air = readFlag(school_names, "air");
+    spell->earth = readFlag(school_names, "earth");
+    spell->fire = readFlag(school_names, "fire");
+    spell->water = readFlag(school_names, "water");
 
-		auto read_node = [&](std::string name, std::vector<Bonus::BonusType> &vec)
-		{
-			const JsonNode & node = spell.second[name];
+    spell->level = json["level"].Float();
+    spell->power = json["power"].Float();
 
-			if (!node.isNull())
-			{
-				auto names = node.convertTo<std::vector<std::string> >();
-				for(auto name : names)
-				   find_in_map(name, vec);
-			}
-		};
+    //TODO: default chance
+    spell->defaultProbability = json["defaultGainChance"].Float();
 
-		read_node("immunity",s->immunities);
-		read_node("limit",s->limiters);
+    auto chances = json["gainChance"].Struct();
+
+    for(auto &node : chances)
+	{
+		int chance = node.second.Float();
 
-		const JsonNode & graphicsNode = spell.second["graphics"];
-		if (!graphicsNode.isNull())
+		VLC->modh->identifiers.requestIdentifier(node.second.meta, "faction",node.first, [=](si32 factionID)
 		{
-			s->iconImmune = graphicsNode["iconImmune"].String();
-		}
+			spell->probabilities[factionID] = chance;
+		});
 	}
+
+
+    auto target_type_str = json["targetType"].String();
+
+    if (target_type_str == "NO_TARGET")
+        spell->targetType = CSpell::NO_TARGET;
+    else if (target_type_str == "CREATURE")
+        spell->targetType = CSpell::CREATURE;
+    else if (target_type_str == "OBSTACLE")
+        spell->targetType = CSpell::OBSTACLE;
+    else if (target_type_str == "CREATURE_EXPERT_MASSIVE")
+        spell->targetType = CSpell::CREATURE_EXPERT_MASSIVE;
+    else
+    {
+        logGlobal->errorStream() << spell->name << ": invalid target type '" <<target_type_str<<"'";
+        fatalConfigurationError();
+    }
+
+    spell->mainEffectAnim = json["anim"].Float();
+
+    for(const auto& k_v: json["counters"].Struct())
+    {
+        if (k_v.second.Bool())
+        {
+            JsonNode tmp(JsonNode::DATA_STRING);
+            tmp.meta = json.meta;
+            tmp.String() = k_v.first;
+
+            VLC->modh->identifiers.requestIdentifier(tmp,[=](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
+
+    if (readFlag(flags,"summoning"))
+    {
+        logGlobal->warnStream() << spell->name << ": summoning flag in unimplemented";
+    }
+
+    spell->isDamage = readFlag(flags,"damage"); //do this before "offensive"
+
+    if (readFlag(flags,"offensive"))
+    {
+        spell->setIsOffensive(true);
+    }
+
+    if (readFlag(flags,"rising"))
+    {
+        spell->setIsRising(true);
+    }
+
+    const bool implicit_positiveness = spell->isOffensive || spell->isRising; //(!) "damage" does not mean NEGATIVE  --AVS
+
+    if (readFlag(flags,"indifferent"))
+    {
+        spell->positiveness = CSpell::NEUTRAL;
+    }
+    else if (readFlag(flags,"negative"))
+    {
+        spell->positiveness = CSpell::NEGATIVE;
+    }
+    else if (readFlag(flags,"positive"))
+    {
+        spell->positiveness = CSpell::POSITIVE;
+    }
+    else if(!implicit_positiveness)
+    {
+        spell->positiveness = CSpell::NEUTRAL; //duplicates constructor but, just in case
+        logGlobal->errorStream() << "No positiveness specified, assumed NEUTRAL";
+    }
+
+
+
+    auto find_in_map = [&](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 read_node = [&](std::string name, std::vector<Bonus::BonusType> &vec)
+    {
+        const JsonNode & node = json[name];
+
+        if (!node.isNull())
+        {
+            for (auto key_value: node.Struct())
+            {
+                const std::string bonus_id = key_value.first;
+                const bool flag = key_value.second.Bool();
+
+                if (flag)
+                {
+                   find_in_map(bonus_id, vec);
+                }
+            }
+        }
+    };
+
+    read_node("immunity",spell->immunities);
+
+    read_node("absoluteImmunity", spell->absoluteImmunities);
+
+    read_node("limit",spell->limiters);
+
+
+    const JsonNode & graphicsNode = json["graphics"];
+    if (!graphicsNode.isNull())
+    {
+        spell->iconImmune = graphicsNode["iconImmune"].String();
+    }
+
+    //load level attributes
+
+    const int level_count = GameConstants::SPELL_SCHOOL_LEVELS;
+
+    spell->AIVals.resize(level_count);
+    spell->costs.resize(level_count);
+    spell->descriptions.resize(level_count);
+
+    spell->powers.resize(level_count);
+    spell->range.resize(level_count);
+
+
+    const JsonNode & levels_node = json["levels"];
+
+    if (levels_node.isNull())
+    {
+        logGlobal->errorStream() << spell->name << ": no level specific data";
+        fatalConfigurationError();
+    }
+
+    if (levels_node.getType()!=JsonNode::DATA_STRUCT)
+    {
+        logGlobal->errorStream() << spell->name << ": level specific data shall be JSON object";
+        fatalConfigurationError();
+    }
+
+    const JsonMap & levels = json["levels"].Struct();
+
+
+    for(int level_idx = 0; level_idx < level_count; level_idx++)
+    {
+        const auto& level_node = levels.at(level_names[level_idx]);
+
+
+        if (level_node.getType()!=JsonNode::DATA_STRUCT)
+        {
+            logGlobal->errorStream() << spell->name << ": level specific data shall be JSON object";
+            fatalConfigurationError();
+        }
+
+        auto ensure_field = [&](const std::string json_name,JsonNode::JsonType type)->JsonNode
+        {
+            const auto& node = level_node[json_name];
+
+            if (node.isNull())
+            {
+                logGlobal->errorStream() << spell->name << ": mandatory field "<<json_name<<" missing";
+                fatalConfigurationError();
+            }
+
+            if (node.getType()!=type)
+            {
+                logGlobal->errorStream() << spell->name << ": field "<<json_name<<" - type mismatch";
+                fatalConfigurationError();
+            }
+            return node;
+        };
+
+
+        auto get_string_mandatory = [&](const std::string json_name, std::vector<std::string>& target)
+        {
+            const auto& node = ensure_field(json_name, JsonNode::DATA_STRING);
+            target[level_idx] = node.String();
+
+        };
+
+        auto get_string = [&](const std::string json_name, std::vector<std::string>& target)
+        {
+            const auto& node = level_node[json_name];
+            if (node.getType() == JsonNode::DATA_STRING)
+            {
+                target[level_idx] = node.String();
+            }
+        };
+
+        auto get_nomber = [&](const std::string json_name, std::vector<si32>& target)
+        {
+            const auto& node = level_node[json_name];
+            if (node.getType() == JsonNode::DATA_FLOAT)
+            {
+                target[level_idx] = node.Float();
+            }
+        };
+
+        auto get_nomber_mandatory = [&](const std::string json_name, std::vector<si32>& target)
+        {
+            const auto& node = ensure_field(json_name, JsonNode::DATA_FLOAT);
+            target[level_idx] = node.Float();
+        };
+
+        if (spell->isCreatureAbility())
+        {
+            get_string("description", spell->descriptions);
+            get_nomber("cost", spell->costs);
+            get_nomber("power", spell->powers);
+            get_nomber("aiValue", spell->AIVals);
+
+        }
+        else
+        {
+            get_string_mandatory("description", spell->descriptions);
+            get_nomber_mandatory("cost", spell->costs);
+            get_nomber_mandatory("power", spell->powers);
+            get_nomber_mandatory("aiValue", spell->AIVals);
+        }
+
+
+
+        const JsonNode& effects_node = level_node["effects"];
+
+        if (!effects_node.isNull())
+        {
+            if (spell->effects.empty())
+                spell->effects.resize(level_count);
+
+            for (const auto& elem : effects_node.Struct())
+            {
+                const JsonNode& bonus_node = elem.second;
+                Bonus * b = JsonUtils::parseBonus(bonus_node);
+                const bool usePowerAsValue = bonus_node["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 = spell->powers[level_idx];
+                }
+
+				spell->effects[level_idx].push_back(b);
+
+            }
+        }
+    }
+
+    return spell;
+}
+
+void CSpellHandler::afterLoadFinalization()
+{
+    //FIXME: this a bad place for this code, should refactor loadFromJson to know object id during load
+
+    for (auto spell: objects)
+        for (auto & level: spell->effects)
+            for (auto * bonus: level)
+                bonus->sid = spell->id;
+
+
 }
 
+
+
 CSpellHandler::~CSpellHandler()
 {
-	for(auto & spell : spells)
-	{
-		spell.dellNull();
-	}
+
 }
 
 std::vector<bool> CSpellHandler::getDefaultAllowed() const

+ 87 - 13
lib/CSpellHandler.h

@@ -1,10 +1,12 @@
 #pragma once
 
+#include "IHandlerBase.h"
 #include "../lib/ConstTransitivePtr.h"
 #include "int3.h"
 #include "GameConstants.h"
 #include "HeroBonus.h"
 
+
 /*
  * CSpellHandler.h, part of VCMI engine
  *
@@ -20,11 +22,24 @@ struct BattleHex;
 
 class DLL_LINKAGE CSpell
 {
+public:
+//    struct LevelInfo
+//    {
+//
+//    };
+//
+//    /** \brief Low level accessor. Don`t use it if absolutely necessary
+//     *
+//     * \param level. spell school level
+//     * \return Spell level info structure
+//     *
+//     */
+//    const LevelInfo& getLevelInfo(const int level);
 public:
 	enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE};
 	enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
 	SpellID id;
-	std::string identifier;
+	std::string identifier; //???
 	std::string name;
 	std::string abbName; //abbreviated name
 	std::vector<std::string> descriptions; //descriptions of spell for skill levels: 0 - none, 1 - basic, etc
@@ -34,10 +49,8 @@ public:
 	bool fire;
 	bool air;
 	si32 power; //spell's power
-	std::vector<si32> costs; //per skill level: 0 - none, 1 - basic, etc
-	std::vector<si32> powers; //[er skill level: 0 - none, 1 - basic, etc
+
 	std::map<TFaction, si32> probabilities; //% chance to gain for castles
-	std::vector<si32> AIVals; //AI values: per skill level: 0 - none, 1 - basic, etc
 
 	bool combatSpell; //is this spell combat (true) or adventure (false)
 	bool creatureAbility; //if true, only creatures can use this spell
@@ -68,6 +81,21 @@ public:
 
 	bool isImmuneBy(const IBonusBearer *obj) const;
 
+	inline si32 getCost(const int skillLevel) const;
+
+    /**
+	* Returns spell level power, base power ignored
+	*/
+	inline si32 getPower(const int skillLevel) const;
+
+//    /**
+//	* Returns spell power, taking base power into account
+//	*/
+//	inline si32 calculatePower(const int skillLevel) const;
+
+
+    inline si32 getProbability(const TFaction factionId) const;
+
 	/**
 	* Returns resource name of icon for SPELL_IMMUNITY bonus
 	*/
@@ -81,29 +109,42 @@ public:
 		h & targetType;
 		h & effects & immunities & limiters;
 		h & iconImmune;
+        h & absoluteImmunities & defaultProbability;
 	}
 	friend class CSpellHandler;
 
 private:
+    void setIsOffensive(const bool val);
+    void setIsRising(const bool val);
+    void setAttributes(const std::string& newValue);
 
+private:
+    si32 defaultProbability;
+	std::vector<si32> costs; //per skill level: 0 - none, 1 - basic, etc
+	std::vector<si32> powers; //per skill level: 0 - none, 1 - basic, etc
+	std::vector<si32> AIVals; //AI values: per skill level: 0 - none, 1 - basic, etc
 
 	bool isRising;
 	bool isDamage;
 	bool isOffensive;
 
-	std::string attributes; //reference only attributes
+	std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
+
 
-	void setAttributes(const std::string& newValue);
 
 	ETargetType targetType;
 
 	std::vector<std::vector<Bonus *> > effects; // [level 0-3][list of effects]
 	std::vector<Bonus::BonusType> immunities; //any of these grants immunity
+	std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, cant be negated
 	std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
 
 	///graphics related stuff
 
 	std::string iconImmune;
+
+
+
 };
 
 ///CSpell inlines
@@ -150,7 +191,7 @@ bool CSpell::isOffensiveSpell() const
 
 bool CSpell::hasEffects() const
 {
-	return effects.size();
+	return effects.size() && effects[0].size();
 }
 
 const std::string& CSpell::getIconImmune() const
@@ -158,27 +199,60 @@ const std::string& CSpell::getIconImmune() const
 	return iconImmune;
 }
 
+si32 CSpell::getCost(const int skillLevel) const
+{
+    return costs[skillLevel];
+}
+
+si32 CSpell::getPower(const int skillLevel) const
+{
+    return powers[skillLevel];
+}
+
+//si32 CSpell::calculatePower(const int skillLevel) const
+//{
+//    return power + getPower(skillLevel);
+//}
+
+si32 CSpell::getProbability(const TFaction factionId) const
+{
+    if (! vstd::contains(probabilities,factionId))
+    {
+        return defaultProbability;
+    }
+    return probabilities.at(factionId);
+}
+
 
 bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door
 
-class DLL_LINKAGE CSpellHandler
+class DLL_LINKAGE CSpellHandler: public CHandlerBase<SpellID, CSpell>
 {
-	CSpell * loadSpell(CLegacyConfigParser & parser, const SpellID id);
+
+	//CSpell * loadSpell(CLegacyConfigParser & parser, const SpellID id);
 
 public:
 	CSpellHandler();
-	~CSpellHandler();
-	std::vector< ConstTransitivePtr<CSpell> > spells;
+	virtual ~CSpellHandler();
+
+    ///IHandler base
+
+    std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
+    void afterLoadFinalization() 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;
+	std::vector<bool> getDefaultAllowed() const override;
+
+	const std::string getTypeName() override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & spells ;
+		h & objects ;
 	}
+protected:
+    CSpell * loadFromJson(const JsonNode & json) override;
 };

+ 13 - 13
lib/Connection.h

@@ -28,7 +28,7 @@
 #include "mapping/CCampaignHandler.h" //for CCampaignState
 #include "rmg/CMapGenerator.h" // for CMapGenOptions
 
-const ui32 version = 745;
+const ui32 version = 746;
 const ui32 minSupportedVersion = version;
 
 class CConnection;
@@ -120,11 +120,11 @@ struct PointerCaster : IPointerCaster
 		}
 	}
 
-	virtual boost::any castSharedPtr(const boost::any &ptr) const override 
+	virtual boost::any castSharedPtr(const boost::any &ptr) const override
 	{
 		return castSmartPtr<std::shared_ptr<From>>(ptr);
 	}
-	virtual boost::any castWeakPtr(const boost::any &ptr) const override 
+	virtual boost::any castWeakPtr(const boost::any &ptr) const override
 	{
 		auto from = boost::any_cast<std::weak_ptr<From>>(ptr);
 		return castSmartPtr<std::shared_ptr<From>>(from.lock());
@@ -155,7 +155,7 @@ private:
 	{
 		// This type is non-copyable.
 		// Unfortunately on Windows it is required for DLL_EXPORT-ed type to provide copy c-tor, so we can't =delete it.
-		assert(0); 
+		assert(0);
 	}
 	CTypeList &operator=(CTypeList &)
 	{
@@ -170,7 +170,7 @@ public:
 	TypeInfoPtr registerType(const std::type_info *type);
 
 
-	template <typename Base, typename Derived> 
+	template <typename Base, typename Derived>
 	void registerType(const Base * b = nullptr, const Derived * d = nullptr)
 	{
 		static_assert(std::is_base_of<Base, Derived>::value, "First registerType template parameter needs to ba a base class of the second one.");
@@ -189,12 +189,12 @@ public:
 	ui16 getTypeID(const std::type_info *type);
 	TypeInfoPtr getTypeDescriptor(const std::type_info *type, bool throws = true); //if not throws, failure returns nullptr
 
-	template <typename T> 
+	template <typename T>
 	ui16 getTypeID(const T * t = nullptr)
 	{
 		return getTypeID(getTypeInfo(t));
 	}
-	
+
 
 	// Returns sequence of types starting from "from" and ending on "to". Every next type is derived from the previous.
 	// Throws if there is no link registered.
@@ -1301,7 +1301,7 @@ public:
 		typedef typename boost::remove_const<T>::type NonConstT;
 		NonConstT *internalPtr;
 		*this >> internalPtr;
-		
+
 		void *internalPtrDerived = typeList.castToMostDerived(internalPtr);
 
 		if(internalPtr)
@@ -1309,7 +1309,7 @@ public:
 			auto itr = loadedSharedPointers.find(internalPtrDerived);
 			if(itr != loadedSharedPointers.end())
 			{
-				// This pointers is already loaded. The "data" needs to be pointed to it, 
+				// This pointers is already loaded. The "data" needs to be pointed to it,
 				// so their shared state is actually shared.
 				try
 				{
@@ -1329,8 +1329,8 @@ public:
 				}
 				catch(std::exception &e)
 				{
-					logGlobal->errorStream() << e.what(); 
-					logGlobal->errorStream() << boost::format("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME") 
+					logGlobal->errorStream() << e.what();
+					logGlobal->errorStream() << boost::format("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME")
 						% itr->second.type().name() % typeid(std::shared_ptr<T>).name();
 					//TODO scenario with inheritance -> we can have stored ptr to base and load ptr to derived (or vice versa)
 					assert(0);
@@ -1549,7 +1549,7 @@ class DLL_LINKAGE CLoadIntegrityValidator : public CISer<CLoadIntegrityValidator
 public:
 	unique_ptr<CLoadFile> primaryFile, controlFile;
 	bool foundDesync;
-	
+
 	CLoadIntegrityValidator(const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion = version); //throws!
 
 	int read( void * data, unsigned size); //throws!
@@ -1661,7 +1661,7 @@ public:
 		}
 	}
 
-	template<typename Base, typename Derived> 
+	template<typename Base, typename Derived>
 	void registerType(const Base * b = nullptr, const Derived * d = nullptr)
 	{
 		typeList.registerType(b, d);

+ 1 - 1
lib/GameConstants.cpp

@@ -87,7 +87,7 @@ CCreature * CreatureID::toCreature() const
 
 CSpell * SpellID::toSpell() const
 {
-	return VLC->spellh->spells[*this];
+	return VLC->spellh->objects[*this];
 }
 
 //template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);

+ 59 - 1
lib/IHandlerBase.h

@@ -9,6 +9,9 @@
  * Full text of license available in license.txt file, in main folder
  *
  */
+ #include "../lib/ConstTransitivePtr.h"
+ #include "VCMI_Lib.h"
+ #include "CModHandler.h"
 
 class JsonNode;
 
@@ -40,4 +43,59 @@ public:
 	virtual std::vector<bool> getDefaultAllowed() const = 0;
 
 	virtual ~IHandlerBase(){}
-};
+};
+
+
+template <class _ObjectID, class _Object> class CHandlerBase: public IHandlerBase
+{
+public:
+    virtual ~CHandlerBase()
+    {
+        for(auto & o : objects)
+        {
+            o.dellNull();
+        }
+
+    }
+	void loadObject(std::string scope, std::string name, const JsonNode & data) override
+	{
+	    auto type_name = getTypeName();
+        auto object = loadFromJson(data);
+        object->id = _ObjectID(objects.size());
+
+        objects.push_back(object);
+
+        VLC->modh->identifiers.registerObject(scope, type_name, name, object->id);
+	}
+	void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override
+	{
+	    auto type_name = getTypeName();
+        auto object = loadFromJson(data);
+        object->id = _ObjectID(index);
+
+
+        assert(objects[index] == nullptr); // ensure that this id was not loaded before
+        objects[index] = object;
+
+        VLC->modh->identifiers.registerObject(scope,type_name, name, object->id);
+
+	}
+
+	ConstTransitivePtr<_Object> operator[] (const _ObjectID id) const
+	{
+	    const auto raw_id = id.toEnum();
+
+	    if (raw_id < 0 || raw_id >= objects.size())
+	    {
+	        logGlobal->errorStream() << getTypeName() << " id " << static_cast<si64>(raw_id) << "is invalid";
+	        throw std::runtime_error ("internal error");
+	    }
+
+	    return objects[raw_id];
+	}
+protected:
+    virtual _Object * loadFromJson(const JsonNode & json) = 0;
+    virtual const std::string getTypeName() = 0;
+public: //todo: make private
+    std::vector<ConstTransitivePtr<_Object>> objects;
+};

+ 6 - 0
lib/NetPacksLib.cpp

@@ -1378,6 +1378,12 @@ void actualizeEffect(CStack * s, const Bonus & ef)
 
 DLL_LINKAGE void SetStackEffect::applyGs( CGameState *gs )
 {
+    if (effect.empty())
+    {
+        logGlobal->errorStream() << "Trying to apply SetStackEffect with no effects";
+        return;
+    }
+
 	int spellid = effect.begin()->sid; //effects' source ID
 
 	for(ui32 id : stacks)

+ 11 - 5
lib/VCMI_lib.cbp

@@ -17,8 +17,8 @@
 				<Option run_host_application_in_terminal="1" />
 				<Option createStaticLib="1" />
 				<Compiler>
-					<Add option="-O1" />
-					<Add option="-O" />
+					<Add option="-Og" />
+					<Add option="-g" />
 				</Compiler>
 			</Target>
 			<Target title="Release">
@@ -64,7 +64,7 @@
 			<Add option="-lboost_thread$(#boost.libsuffix)" />
 			<Add option="-lboost_chrono$(#boost.libsuffix)" />
 			<Add option="-lboost_locale$(#boost.libsuffix)" />
-			<Add directory="$(#boost.lib)" />
+			<Add directory="$(#boost.lib32)" />
 			<Add directory="$(#sdl.lib)" />
 			<Add directory="../" />
 		</Linker>
@@ -140,8 +140,6 @@
 		<Unit filename="NetPacks.h" />
 		<Unit filename="NetPacksBase.h" />
 		<Unit filename="NetPacksLib.cpp" />
-		<Unit filename="RegisterTypes.cpp" />
-		<Unit filename="RegisterTypes.h" />
 		<Unit filename="ResourceSet.cpp" />
 		<Unit filename="ResourceSet.h" />
 		<Unit filename="ScopeGuard.h" />
@@ -198,6 +196,14 @@
 		<Unit filename="mapping/MapFormatH3M.h" />
 		<Unit filename="mapping/MapFormatJson.cpp" />
 		<Unit filename="mapping/MapFormatJson.h" />
+		<Unit filename="registerTypes/RegisterTypes.cpp" />
+		<Unit filename="registerTypes/RegisterTypes.h" />
+		<Unit filename="registerTypes/TypesClientPacks1.cpp" />
+		<Unit filename="registerTypes/TypesClientPacks2.cpp" />
+		<Unit filename="registerTypes/TypesMapObjects1.cpp" />
+		<Unit filename="registerTypes/TypesMapObjects2.cpp" />
+		<Unit filename="registerTypes/TypesPregamePacks.cpp" />
+		<Unit filename="registerTypes/TypesServerPacks.cpp" />
 		<Unit filename="rmg/CMapGenOptions.cpp" />
 		<Unit filename="rmg/CMapGenOptions.h" />
 		<Unit filename="rmg/CMapGenerator.cpp" />

+ 3 - 1
lib/registerTypes/RegisterTypes.cpp

@@ -37,7 +37,9 @@ extern template DLL_LINKAGE void METHODNAME<CLoadFile>(CLoadFile & s); \
 extern template DLL_LINKAGE void METHODNAME<CTypeList>(CTypeList & s); \
 extern template DLL_LINKAGE void METHODNAME<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);
 
-DEFINE_EXTERNAL_METHOD(registerTypesMapObjects)
+//DEFINE_EXTERNAL_METHOD(registerTypesMapObjects)
+DEFINE_EXTERNAL_METHOD(registerTypesMapObjects1)
+DEFINE_EXTERNAL_METHOD(registerTypesMapObjects2)
 DEFINE_EXTERNAL_METHOD(registerTypesClientPacks1)
 DEFINE_EXTERNAL_METHOD(registerTypesClientPacks2)
 DEFINE_EXTERNAL_METHOD(registerTypesServerPacks)

+ 13 - 8
lib/registerTypes/RegisterTypes.h

@@ -20,12 +20,14 @@
  *
  */
 
+
+
 template<typename Serializer>
-void registerTypesMapObjects(Serializer &s)
+void registerTypesMapObjects1(Serializer &s)
 {
 	//////////////////////////////////////////////////////////////////////////
-	// Adventure map objects (and related)
-	////////////////////////////////////////////////////////////////////////// 
+	// Adventure map objects
+	//////////////////////////////////////////////////////////////////////////
 	s.template registerType<IObjectInterface, CGObjectInstance>();
 
 	// Non-armed objects
@@ -68,8 +70,11 @@ void registerTypesMapObjects(Serializer &s)
 		s.template registerType<CBank, CGPyramid>();
 	s.template registerType<CArmedInstance, CGSeerHut>(); s.template registerType<IQuestObject, CGSeerHut>();
 	s.template registerType<CGSeerHut, CGQuestGuard>();
+}
 
-
+template<typename Serializer>
+void registerTypesMapObjects2(Serializer &s)
+{
 	//Other object-related
 	s.template registerType<IObjectInterface, CGTownBuilding>();
 		s.template registerType<CGTownBuilding, CTownBonus>();
@@ -95,7 +100,7 @@ void registerTypesMapObjects(Serializer &s)
 
 	//////////////////////////////////////////////////////////////////////////
 	// Bonus system
-	////////////////////////////////////////////////////////////////////////// 
+	//////////////////////////////////////////////////////////////////////////
 	//s.template registerType<IPropagator>();
 	s.template registerType<IPropagator, CPropagatorNodeType>();
 
@@ -109,7 +114,7 @@ void registerTypesMapObjects(Serializer &s)
 	s.template registerType<ILimiter, CreatureAlignmentLimiter>();
 	s.template registerType<ILimiter, RankRangeLimiter>();
 	s.template registerType<ILimiter, StackOwnerLimiter>();
-	
+
 //	s.template registerType<CBonusSystemNode>();
 	s.template registerType<CBonusSystemNode, CArtifact>();
 	s.template registerType<CArtifact, CGrowingArtifact>();
@@ -131,7 +136,6 @@ void registerTypesMapObjects(Serializer &s)
 		s.template registerType<CObstacleInstance, MoatObstacle>();
 		s.template registerType<CObstacleInstance, SpellCreatedObstacle>();
 }
-
 template<typename Serializer>
 void registerTypesClientPacks1(Serializer &s)
 {
@@ -295,7 +299,8 @@ void registerTypesPregamePacks(Serializer &s)
 template<typename Serializer>
 void registerTypes(Serializer &s)
 {
-	registerTypesMapObjects(s);
+	registerTypesMapObjects1(s);
+	registerTypesMapObjects2(s);
 	registerTypesClientPacks1(s);
 	registerTypesClientPacks2(s);
 	registerTypesServerPacks(s);

+ 0 - 29
lib/registerTypes/TypesMapObjects.cpp

@@ -1,29 +0,0 @@
-#include "StdInc.h"
-#include "RegisterTypes.h"
-
-#include "../mapping/CMapInfo.h"
-#include "../StartInfo.h"
-#include "../BattleState.h"
-#include "../CGameState.h"
-#include "../mapping/CMap.h"
-#include "../CModHandler.h"
-#include "../CObjectHandler.h"
-#include "../CCreatureHandler.h"
-#include "../VCMI_Lib.h"
-#include "../CArtHandler.h"
-#include "../CHeroHandler.h"
-#include "../CSpellHandler.h"
-#include "../CTownHandler.h"
-#include "../mapping/CCampaignHandler.h"
-#include "../NetPacks.h"
-#include "../CDefObjInfoHandler.h"
-
-
-template void registerTypesMapObjects<CISer<CConnection>>(CISer<CConnection>& s);
-template void registerTypesMapObjects<COSer<CConnection>>(COSer<CConnection>& s);
-template void registerTypesMapObjects<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
-template void registerTypesMapObjects<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
-template void registerTypesMapObjects<CSaveFile>(CSaveFile & s);
-template void registerTypesMapObjects<CLoadFile>(CLoadFile & s);
-template void registerTypesMapObjects<CTypeList>(CTypeList & s);
-template void registerTypesMapObjects<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

+ 30 - 0
lib/registerTypes/TypesMapObjects1.cpp

@@ -0,0 +1,30 @@
+#include "StdInc.h"
+#include "RegisterTypes.h"
+
+#include "mapping/CMapInfo.h"
+#include "StartInfo.h"
+#include "BattleState.h"
+#include "CGameState.h"
+#include "mapping/CMap.h"
+#include "CModHandler.h"
+#include "CObjectHandler.h"
+#include "CCreatureHandler.h"
+#include "VCMI_Lib.h"
+#include "CArtHandler.h"
+#include "CHeroHandler.h"
+#include "CSpellHandler.h"
+#include "CTownHandler.h"
+#include "mapping/CCampaignHandler.h"
+#include "NetPacks.h"
+#include "CDefObjInfoHandler.h"
+
+
+template void registerTypesMapObjects1<CISer<CConnection>>(CISer<CConnection>& s);
+template void registerTypesMapObjects1<COSer<CConnection>>(COSer<CConnection>& s);
+template void registerTypesMapObjects1<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
+template void registerTypesMapObjects1<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
+template void registerTypesMapObjects1<CSaveFile>(CSaveFile & s);
+template void registerTypesMapObjects1<CLoadFile>(CLoadFile & s);
+template void registerTypesMapObjects1<CTypeList>(CTypeList & s);
+template void registerTypesMapObjects1<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);
+

+ 30 - 0
lib/registerTypes/TypesMapObjects2.cpp

@@ -0,0 +1,30 @@
+#include "StdInc.h"
+#include "RegisterTypes.h"
+
+#include "mapping/CMapInfo.h"
+#include "StartInfo.h"
+#include "BattleState.h"
+#include "CGameState.h"
+#include "mapping/CMap.h"
+#include "CModHandler.h"
+#include "CObjectHandler.h"
+#include "CCreatureHandler.h"
+#include "VCMI_Lib.h"
+#include "CArtHandler.h"
+#include "CHeroHandler.h"
+#include "CSpellHandler.h"
+#include "CTownHandler.h"
+#include "mapping/CCampaignHandler.h"
+#include "NetPacks.h"
+#include "CDefObjInfoHandler.h"
+
+
+template void registerTypesMapObjects2<CISer<CConnection>>(CISer<CConnection>& s);
+template void registerTypesMapObjects2<COSer<CConnection>>(COSer<CConnection>& s);
+template void registerTypesMapObjects2<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
+template void registerTypesMapObjects2<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
+template void registerTypesMapObjects2<CSaveFile>(CSaveFile & s);
+template void registerTypesMapObjects2<CLoadFile>(CLoadFile & s);
+template void registerTypesMapObjects2<CTypeList>(CTypeList & s);
+template void registerTypesMapObjects2<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);
+

+ 31 - 31
server/CGameHandler.cpp

@@ -493,11 +493,11 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 	}
 	if(battleQuery != queries.topQuery(gs->curB->sides[0].color))
 		complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " although in battle has no battle query at the top!");
-		
+
 	battleQuery->result = *battleResult.data;
 
 	//Check how many battle queries were created (number of players blocked by battle)
-	const int queriedPlayers = battleQuery ? boost::count(queries.allQueries(), battleQuery) : 0; 
+	const int queriedPlayers = battleQuery ? boost::count(queries.allQueries(), battleQuery) : 0;
 	finishingBattle = make_unique<FinishingBattleHelper>(battleQuery, gs->initialOpts->mode == StartInfo::DUEL, queriedPlayers);
 
 
@@ -651,7 +651,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 		sendAndApply(&iw);
 		sendAndApply(&cs);
 	}
-	
+
 	cab1.takeFromArmy(this);
 	cab2.takeFromArmy(this); //take casualties after battle is deleted
 
@@ -691,7 +691,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
 		return;
 
 	//TODO consider if we really want it to work like above. ATM each player as unblocked as soon as possible
-	// but the battle consequences are applied after final player is unblocked. Hard to abuse... 
+	// but the battle consequences are applied after final player is unblocked. Hard to abuse...
 	// Still, it looks like a hole.
 
 	// Necromancy if applicable.
@@ -704,8 +704,8 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
 	{
 		finishingBattle->winnerHero->showNecromancyDialog(raisedStack);
 		addToSlot(StackLocation(finishingBattle->winnerHero, necroSlot), raisedStack.type, raisedStack.count);
-	}	
-	
+	}
+
 	BattleResultsApplied resultsApplied;
 	resultsApplied.player1 = finishingBattle->victor;
 	resultsApplied.player2 = finishingBattle->loser;
@@ -749,7 +749,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 	bat.bsa.clear();
 	bat.stackAttacking = att->ID;
 	const int attackerLuck = att->LuckVal();
-	
+
 	auto sideHeroBlocksLuck = [](const SideInBattle &side){ return NBonus::hasOfType(side.hero, Bonus::BLOCK_LUCK); };
 
 	if(!vstd::contains_if(gs->curB->sides, sideHeroBlocksLuck))
@@ -802,7 +802,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 	if (bonus && (bat.shot())) //TODO: make it work in meele?
 	{
 		bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
-		bat.bsa.front().effect = VLC->spellh->spells.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect?
+		bat.bsa.front().effect = VLC->spellh->objects.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect?
 
 		std::set<const CStack*> attackedCreatures = gs->curB->getAffectedCreatures(SpellID(bonus->subtype).toSpell(), bonus->val, att->owner, targetHex);
 		//TODO: get exact attacked hex for defender
@@ -1712,7 +1712,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, Pl
 		this->getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadious(), h->tempOwner, 1);
 	};
 
-	auto doMove = [&](TryMoveHero::EResult result, EGuardLook lookForGuards, 
+	auto doMove = [&](TryMoveHero::EResult result, EGuardLook lookForGuards,
 								EVisitDest visitDest, ELEaveTile leavingTile) -> bool
 	{
 		LOG_TRACE_PARAMS(logGlobal, "Hero %s starts movement from %s to %s", h->name % tmh.start % tmh.end);
@@ -1732,7 +1732,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, Pl
 
 			const TerrainTile &guardTile = *gs->getTile(guardPos);
 			objectVisited(guardTile.visitableObjects.back(), h);
-			
+
 			moveQuery->visitDestAfterVictory = visitDest==VISIT_DEST;
 		}
 		else if(visitDest == VISIT_DEST)
@@ -1794,8 +1794,8 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, Pl
 
 	//still here? it is standard movement!
 	{
-		tmh.movePoints = h->movement >= cost 
-						? h->movement - cost 
+		tmh.movePoints = h->movement >= cost
+						? h->movement - cost
 						: 0;
 
 		if(blockingVisit())
@@ -1982,8 +1982,8 @@ void CGameHandler::removeArtifact(const ArtifactLocation &al)
 	ea.al = al;
 	sendAndApply(&ea);
 }
-void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, 
-								const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, 
+void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile,
+								const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
 								const CGTownInstance *town) //use hero=nullptr for no hero
 {
 	engageIntoBattle(army1->tempOwner);
@@ -3267,7 +3267,7 @@ static EndAction end_action;
 bool CGameHandler::makeBattleAction( BattleAction &ba )
 {
 	bool ok = true;
-	
+
 	const CStack *stack = battleGetStackByID(ba.stackNumber); //may be nullptr if action is not about stack
 	const CStack *destinationStack = ba.actionType == Battle::WALK_AND_ATTACK ? gs->curB->battleGetStackByPos(ba.additionalInfo)
 								   : ba.actionType == Battle::SHOOT			  ? gs->curB->battleGetStackByPos(ba.destinationTile)
@@ -3276,7 +3276,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 
 	logGlobal->traceStream() << boost::format(
 		"Making action: type=%d; side=%d; stack=%s; dst=%s; additionalInfo=%d; stackAtDst=%s")
-		% ba.actionType % (int)ba.side % (stack ? stack->getName() : std::string("none")) 
+		% ba.actionType % (int)ba.side % (stack ? stack->getName() : std::string("none"))
 		% ba.destinationTile % ba.additionalInfo % (destinationStack ? destinationStack->getName() : std::string("none"));
 
 	switch(ba.actionType)
@@ -3446,7 +3446,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 				{
 					BattleAttack bat;
 					prepareAttack(bat, stack, destinationStack, (i ? 0 : distance),  ba.additionalInfo); //no distance travelled on second attack
-					//prepareAttack(bat, stack, stackAtEnd, 0, ba.additionalInfo); 
+					//prepareAttack(bat, stack, stackAtEnd, 0, ba.additionalInfo);
 					handleAttackBeforeCasting(bat); //only before first attack
 					sendAndApply(&bat);
 					handleAfterAttackCasting(bat);
@@ -3819,7 +3819,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 
 		//give all spells
 		cs.learn = 1;
-		for(auto spell : VLC->spellh->spells)
+		for(auto spell : VLC->spellh->objects)
 		{
 			if(!spell->creatureAbility)
 				cs.spells.insert(spell->id);
@@ -4060,7 +4060,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
 			}
 		}
 	}
-	
+
 	for (auto cre : attackedCres)
 	{
 		sc.affectedCres.insert (cre->ID);
@@ -4354,7 +4354,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
 			int percentBonus = caster ? caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, spellID.toEnum()) : 0;
 
 			bsa.amount = usedSpellPower
-				* SpellID(spellID).toSpell()->powers.at(spellLvl)
+				* SpellID(spellID).toSpell()->getPower(spellLvl)
 				* (100 + percentBonus) / 100.0; //new feature - percentage bonus
 			if(bsa.amount)
 				sendAndApply(&bsa);
@@ -4488,7 +4488,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 	case Battle::HERO_SPELL:
 		{
 			COMPLAIN_RET_FALSE_IF(ba.side > 1, "Side must be 0 or 1!");
-				
+
 
 			const CGHeroInstance *h = gs->curB->battleGetFightingHero(ba.side);
 			const CGHeroInstance *secondHero = gs->curB->battleGetFightingHero(!ba.side);
@@ -4497,7 +4497,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
                 logGlobal->warnStream() << "Wrong caster!";
 				return false;
 			}
-			if(ba.additionalInfo >= VLC->spellh->spells.size())
+			if(ba.additionalInfo >= VLC->spellh->objects.size())
 			{
                 logGlobal->warnStream() << "Wrong spell id (" << ba.additionalInfo << ")!";
 				return false;
@@ -4920,7 +4920,7 @@ void CGameHandler::showGarrisonDialog( ObjectInstanceID upobj, ObjectInstanceID
 	//PlayerColor player = getOwner(hid);
 	auto upperArmy = dynamic_cast<const CArmedInstance*>(getObj(upobj));
 	auto lowerArmy = dynamic_cast<const CArmedInstance*>(getObj(hid));
-	
+
 	assert(lowerArmy);
 	assert(upperArmy);
 
@@ -5007,7 +5007,7 @@ void CGameHandler::objectVisited( const CGObjectInstance * obj, const CGHeroInst
 
 	obj->onHeroVisit(h);
 
-	queries.popIfTop(visitQuery); //visit ends here if no queries were created 
+	queries.popIfTop(visitQuery); //visit ends here if no queries were created
 }
 
 void CGameHandler::objectVisitEnded(const CObjectVisitQuery &query)
@@ -5223,7 +5223,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			checkVictoryLossConditions(playerColors);
 		}
 
-		auto playerInfo = gs->getPlayer(gs->currentPlayer, false);  
+		auto playerInfo = gs->getPlayer(gs->currentPlayer, false);
 		// If we are called before the actual game start, there might be no current player
 		if(playerInfo && playerInfo->status != EPlayerStatus::INGAME)
 		{
@@ -5433,7 +5433,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
 	case SpellID::SUMMON_BOAT:
 		{
 			//check if spell works at all
-			if(rand() % 100 >= s->powers.at(schoolLevel)) //power is % chance of success
+			if(rand() % 100 >= s->getPower(schoolLevel)) //power is % chance of success
 			{
 				InfoWindow iw;
 				iw.player = h->tempOwner;
@@ -5495,7 +5495,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
 	case SpellID::SCUTTLE_BOAT:
 		{
 			//check if spell works at all
-			if(rand() % 100 >= s->powers.at(schoolLevel)) //power is % chance of success
+			if(rand() % 100 >= s->getPower(schoolLevel)) //power is % chance of success
 			{
 				InfoWindow iw;
 				iw.player = h->tempOwner;
@@ -5526,7 +5526,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
 				COMPLAIN_RET("Destination tile doesn't exist!");
 			if(!h->movement)
 				COMPLAIN_RET("Hero needs movement points to cast Dimension Door!");
-			if(h->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= s->powers.at(schoolLevel)) //limit casts per turn
+			if(h->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= s->getPower(schoolLevel)) //limit casts per turn
 			{
 				InfoWindow iw;
 				iw.player = h->tempOwner;
@@ -5890,7 +5890,7 @@ void CGameHandler::runBattle()
 		std::set <const CStack*> stacksToRemove;
 		for (auto stack : curB.stacks)
 		{
-			if (stack->idDeadClone()) 
+			if (stack->idDeadClone())
 				stacksToRemove.insert(stack);
 		}
 		for (auto stack : stacksToRemove)
@@ -6276,7 +6276,7 @@ void CGameHandler::duelFinished()
 	auto getName = [&](int i){ return si->getIthPlayersSettings(gs->curB->sides.at(i).color).name; };
 
 	int casualtiesPoints = 0;
-	logGlobal->debugStream() << boost::format("Winner side %d\nWinner casualties:") 
+	logGlobal->debugStream() << boost::format("Winner side %d\nWinner casualties:")
 		% (int)battleResult.data->winner;
 
 	for(auto & elem : battleResult.data->casualties[battleResult.data->winner])
@@ -6295,7 +6295,7 @@ void CGameHandler::duelFinished()
 	if(out)
 	{
 		out << boost::format("%s\t%s\t%s\t%d\t%d\t%d\t%s\n") % si->mapname % getName(0) % getName(1)
-			% battleResult.data->winner % battleResult.data->result % casualtiesPoints 
+			% battleResult.data->winner % battleResult.data->result % casualtiesPoints
 			% asctime(localtime(&timeNow));
 	}
 	else

+ 1 - 1
server/VCMI_server.cbp

@@ -57,7 +57,7 @@
 			<Add option="-lboost_thread$(#boost.libsuffix)" />
 			<Add option="-lboost_chrono$(#boost.libsuffix)" />
 			<Add option="-lVCMI_lib" />
-			<Add directory="$(#boost.lib)" />
+			<Add directory="$(#boost.lib32)" />
 			<Add directory="../" />
 		</Linker>
 		<Unit filename="CGameHandler.cpp" />