|  | @@ -23,7 +23,7 @@
 | 
	
		
			
				|  |  |  namespace SpellConfig
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
 | 
	
		
			
				|  |  | -     
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  using namespace boost::assign;
 | 
	
	
		
			
				|  | @@ -130,12 +130,21 @@ namespace SRSLPraserHelpers
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -using namespace SRSLPraserHelpers;
 | 
	
		
			
				|  |  | +CSpell::LevelInfo::LevelInfo()
 | 
	
		
			
				|  |  | +    :description(""),cost(0),power(0),AIValue(0),smartTarget(true),range("0")
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +CSpell::LevelInfo::~LevelInfo()
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  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),
 | 
	
	
		
			
				|  | @@ -143,18 +152,29 @@ CSpell::CSpell():
 | 
	
		
			
				|  |  |  	isRising(false), isDamage(false), isOffensive(false),
 | 
	
		
			
				|  |  |  	targetType(ETargetType::NO_TARGET)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +    levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  CSpell::~CSpell()
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -	for(auto & elem : effects)
 | 
	
		
			
				|  |  | -		for(size_t j=0; j<elem.size(); j++)
 | 
	
		
			
				|  |  | -			delete elem[j];
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +        logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
 | 
	
		
			
				|  |  | +        throw new std::runtime_error("Invalid school level");
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return levels.at(level);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +    using namespace SRSLPraserHelpers;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	std::vector<BattleHex> ret;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if(id == SpellID::FIRE_WALL  ||  id == SpellID::FORCE_FIELD)
 | 
	
	
		
			
				|  | @@ -192,7 +212,7 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	std::string rng = range[schoolLvl] + ','; //copy + artificial comma for easier handling
 | 
	
		
			
				|  |  | +	std::string rng = getLevelInfo(schoolLvl).range + ','; //copy + artificial comma for easier handling
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if(rng.size() >= 1 && rng[0] != 'X') //there is at lest one hex in range
 | 
	
		
			
				|  |  |  	{
 | 
	
	
		
			
				|  | @@ -201,7 +221,7 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
 | 
	
		
			
				|  |  |  		bool readingFirst = true;
 | 
	
		
			
				|  |  |  		for(auto & elem : rng)
 | 
	
		
			
				|  |  |  		{
 | 
	
		
			
				|  |  | -			if(std::isdigit(elem) ) //reading numer
 | 
	
		
			
				|  |  | +			if(std::isdigit(elem) ) //reading number
 | 
	
		
			
				|  |  |  			{
 | 
	
		
			
				|  |  |  				if(readingFirst)
 | 
	
		
			
				|  |  |  					number1 += elem;
 | 
	
	
		
			
				|  | @@ -258,6 +278,20 @@ CSpell::ETargetType CSpell::getTargetType() const
 | 
	
		
			
				|  |  |  	return targetType;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    TargetInfo info;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    auto & levelInfo = getLevelInfo(level);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    info.type = getTargetType();
 | 
	
		
			
				|  |  | +    info.smart = levelInfo.smartTarget;
 | 
	
		
			
				|  |  | +    info.massive = levelInfo.range == "X";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return info;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  bool CSpell::isCombatSpell() const
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	return combatSpell;
 | 
	
	
		
			
				|  | @@ -305,7 +339,7 @@ bool CSpell::isSpecialSpell() const
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  bool CSpell::hasEffects() const
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -	return effects.size() && effects[0].size();
 | 
	
		
			
				|  |  | +	return !levels[0].effects.empty();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const std::string& CSpell::getIconImmune() const
 | 
	
	
		
			
				|  | @@ -322,12 +356,12 @@ const std::string& CSpell::getCastSound() const
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  si32 CSpell::getCost(const int skillLevel) const
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -    return costs[skillLevel];
 | 
	
		
			
				|  |  | +    return getLevelInfo(skillLevel).cost;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  si32 CSpell::getPower(const int skillLevel) const
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -    return powers[skillLevel];
 | 
	
		
			
				|  |  | +    return getLevelInfo(skillLevel).power;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //si32 CSpell::calculatePower(const int skillLevel) const
 | 
	
	
		
			
				|  | @@ -352,27 +386,20 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
 | 
	
		
			
				|  |  |          logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
 | 
	
		
			
				|  |  |  		return;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const std::vector<Bonus> & effects = levels[level].effects;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	if(effects.empty())
 | 
	
		
			
				|  |  | -	{
 | 
	
		
			
				|  |  | -        logGlobal->errorStream() << __FUNCTION__ << " This spell ("  + name + ") has no bonus effects! " << name;
 | 
	
		
			
				|  |  | -		return;
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if(effects.size() <= level)
 | 
	
		
			
				|  |  | -	{
 | 
	
		
			
				|  |  | -		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());
 | 
	
		
			
				|  |  | +	lst.reserve(lst.size() + effects.size());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	for(Bonus *b : effects[level])
 | 
	
		
			
				|  |  | +	for(const Bonus & b : effects)
 | 
	
		
			
				|  |  |  	{
 | 
	
		
			
				|  |  | -		lst.push_back(Bonus(*b));
 | 
	
		
			
				|  |  | +		lst.push_back(Bonus(b));
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -451,20 +478,6 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
 | 
	
		
			
				|  |  |  	return false;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void CSpell::setAttributes(const std::string& newValue)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -	attributes = newValue;
 | 
	
		
			
				|  |  | -	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;
 | 
	
		
			
				|  |  | -	else
 | 
	
		
			
				|  |  | -		targetType = NO_TARGET;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  void CSpell::setIsOffensive(const bool val)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |     isOffensive = val;
 | 
	
	
		
			
				|  | @@ -576,8 +589,6 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
 | 
	
		
			
				|  |  |              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++)
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -588,6 +599,16 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
 | 
	
		
			
				|  |  |                  level["aiValue"].Float() = AIVals[i];
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            if(targetType == "CREATURE_EXPERT_MASSIVE")
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                lineNode["targetType"].String() = "CREATURE";
 | 
	
		
			
				|  |  | +                getLevel(3)["range"].String() = "X";
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                lineNode["targetType"].String() = targetType;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		    legacyData.push_back(lineNode);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -677,8 +698,6 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 | 
	
		
			
				|  |  |  		spell->targetType = CSpell::CREATURE;
 | 
	
		
			
				|  |  |  	else if(targetType == "OBSTACLE")
 | 
	
		
			
				|  |  |  		spell->targetType = CSpell::OBSTACLE;
 | 
	
		
			
				|  |  | -	else if(targetType == "CREATURE_EXPERT_MASSIVE")
 | 
	
		
			
				|  |  | -		spell->targetType = CSpell::CREATURE_EXPERT_MASSIVE;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	spell->mainEffectAnim = json["anim"].Float();
 | 
	
	
		
			
				|  | @@ -767,17 +786,17 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const JsonNode & graphicsNode = json["graphics"];
 | 
	
		
			
				|  |  | - 
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	spell->iconImmune = graphicsNode["iconImmune"].String();
 | 
	
		
			
				|  |  |  	spell->iconBook = graphicsNode["iconBook"].String();
 | 
	
		
			
				|  |  |  	spell->iconEffect = graphicsNode["iconEffect"].String();
 | 
	
		
			
				|  |  |  	spell->iconScenarioBonus = graphicsNode["iconScenarioBonus"].String();
 | 
	
		
			
				|  |  |  	spell->iconScroll = graphicsNode["iconScroll"].String();
 | 
	
		
			
				|  |  | -	
 | 
	
		
			
				|  |  | -	
 | 
	
		
			
				|  |  | -	
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	const JsonNode & soundsNode = json["sounds"];
 | 
	
		
			
				|  |  | -	
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	spell->castSound = soundsNode["cast"].String();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -785,48 +804,37 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    spell->AIVals.resize(levelsCount);
 | 
	
		
			
				|  |  | -    spell->costs.resize(levelsCount);
 | 
	
		
			
				|  |  | -    spell->descriptions.resize(levelsCount);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    spell->powers.resize(levelsCount);
 | 
	
		
			
				|  |  | -    spell->range.resize(levelsCount);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  	for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++)
 | 
	
		
			
				|  |  |  	{
 | 
	
		
			
				|  |  |  		const JsonNode & levelNode = json["levels"][LEVEL_NAMES[levelIndex]];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		spell->descriptions[levelIndex] = levelNode["description"].String();
 | 
	
		
			
				|  |  | -		spell->costs[levelIndex] = levelNode["cost"].Float();
 | 
	
		
			
				|  |  | -		spell->powers[levelIndex] = levelNode["power"].Float();
 | 
	
		
			
				|  |  | -		spell->AIVals[levelIndex] = levelNode["aiValue"].Float();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const JsonNode & effectsNode = levelNode["effects"];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		if(!effectsNode.isNull())
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		CSpell::LevelInfo & levelObject = spell->levels[levelIndex];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		const si32 levelPower   = levelNode["power"].Float();		
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		levelObject.description = levelNode["description"].String();
 | 
	
		
			
				|  |  | +		levelObject.cost        = levelNode["cost"].Float();
 | 
	
		
			
				|  |  | +		levelObject.power       = levelPower;
 | 
	
		
			
				|  |  | +		levelObject.AIValue     = levelNode["aiValue"].Float();
 | 
	
		
			
				|  |  | +		levelObject.smartTarget = levelNode["targetModifier"]["smart"].Bool();
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		for(const auto & elem : levelNode["effects"].Struct())
 | 
	
		
			
				|  |  |  		{
 | 
	
		
			
				|  |  | -			if(spell->effects.empty())
 | 
	
		
			
				|  |  | -				spell->effects.resize(levelsCount);
 | 
	
		
			
				|  |  | +			const JsonNode & bonusNode = elem.second;
 | 
	
		
			
				|  |  | +			Bonus * b = JsonUtils::parseBonus(bonusNode);
 | 
	
		
			
				|  |  | +			const bool usePowerAsValue = bonusNode["val"].isNull();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			for(const auto & elem : effectsNode.Struct())
 | 
	
		
			
				|  |  | -			{
 | 
	
		
			
				|  |  | -				const JsonNode & bonusNode = elem.second;
 | 
	
		
			
				|  |  | -				Bonus * b = JsonUtils::parseBonus(bonusNode);
 | 
	
		
			
				|  |  | -				const bool usePowerAsValue = bonusNode["val"].isNull();
 | 
	
		
			
				|  |  | +			//TODO: make this work. see CSpellHandler::afterLoadFinalization()
 | 
	
		
			
				|  |  | +			//b->sid = spell->id; //for all
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -				//TODO: make this work. see CSpellHandler::afterLoadFinalization()
 | 
	
		
			
				|  |  | -				//b->sid = spell->id; //for all
 | 
	
		
			
				|  |  | -				
 | 
	
		
			
				|  |  | -				b->source = Bonus::SPELL_EFFECT;//for all
 | 
	
		
			
				|  |  | +			b->source = Bonus::SPELL_EFFECT;//for all
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -				if(usePowerAsValue)
 | 
	
		
			
				|  |  | -				{
 | 
	
		
			
				|  |  | -					b->val = spell->powers[levelIndex];
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | +			if(usePowerAsValue)
 | 
	
		
			
				|  |  | +				b->val = levelPower;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -				spell->effects[levelIndex].push_back(b);
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | +			levelObject.effects.push_back(*b);
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      return spell;
 | 
	
	
		
			
				|  | @@ -836,9 +844,9 @@ void CSpellHandler::afterLoadFinalization()
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      //FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
 | 
	
		
			
				|  |  |  	for(auto spell: objects)
 | 
	
		
			
				|  |  | -		for(auto & level: spell->effects)
 | 
	
		
			
				|  |  | -			for(auto * bonus: level)
 | 
	
		
			
				|  |  | -				bonus->sid = spell->id;
 | 
	
		
			
				|  |  | +		for(auto & level: spell->levels)
 | 
	
		
			
				|  |  | +			for(auto & bonus: level.effects)
 | 
	
		
			
				|  |  | +				bonus.sid = spell->id;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 |