Преглед изворни кода

* fixed bug 298
* fixed INDEPENDENT_MIN bonus value type
* minor changes

mateuszb пре 14 година
родитељ
комит
1458cf64c2
10 измењених фајлова са 147 додато и 39 уклоњено
  1. 3 4
      client/CBattleInterface.cpp
  2. 5 0
      client/CSpellWindow.cpp
  3. 2 1
      global.h
  4. 108 25
      lib/BattleState.cpp
  5. 5 1
      lib/BattleState.h
  6. 3 3
      lib/CArtHandler.cpp
  7. 15 0
      lib/CSpellHandler.cpp
  8. 2 0
      lib/CSpellHandler.h
  9. 4 4
      lib/HeroBonus.cpp
  10. 0 1
      lib/HeroBonus.h

+ 3 - 4
client/CBattleInterface.cpp

@@ -2909,7 +2909,7 @@ void CBattleInterface::castThisSpell(int spellID)
 	const CGHeroInstance * castingHero = (attackingHeroInstance->tempOwner == curInt->playerID) ? attackingHeroInstance : defendingHeroInstance;
 	const CSpell & spell = *CGI->spellh->spells[spellID];
 	spellSelMode = 0;
-	if(spell.attributes.find("CREATURE_TARGET") != std::string::npos) //spell to be cast on one specific creature
+	if(spell.getTargetType() == CSpell::CREATURE)
 	{
 		switch(spell.positiveness)
 		{
@@ -2924,8 +2924,7 @@ void CBattleInterface::castThisSpell(int spellID)
 			break;
 		}
 	}
-	if(spell.attributes.find("CREATURE_TARGET_1") != std::string::npos ||
-		spell.attributes.find("CREATURE_TARGET_2") != std::string::npos) //spell to be cast on a specific creature but massive on expert
+	if(spell.getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
 	{
 		if(castingHero && castingHero->getSpellSchoolLevel(&spell) < 3)
 		{
@@ -2947,7 +2946,7 @@ void CBattleInterface::castThisSpell(int spellID)
 			spellSelMode = -1;
 		}
 	}
-	if(spell.attributes.find("OBSTACLE_TARGET") != std::string::npos) //spell to be cast on an obstacle
+	if(spell.getTargetType() == CSpell::OBSTACLE)
 	{
 		spellSelMode = 4;
 	}

+ 5 - 0
client/CSpellWindow.cpp

@@ -665,6 +665,11 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 					owner->myInt->showInfoDialog(text);
 				}
 				break;
+			case SpellCasting::NO_APPROPRIATE_TARGET:
+				{
+					owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[185]);
+				}
+				break;
 			}
 		}
 		else if(!sp->combatSpell && !owner->myInt->battleInt) //adventure spell

+ 2 - 1
global.h

@@ -313,7 +313,8 @@ namespace SpellCasting
 	{
 		OK, NO_HERO_TO_CAST_SPELL, ALREADY_CASTED_THIS_TURN, NO_SPELLBOOK, ANOTHER_ELEMENTAL_SUMMONED,
 		HERO_DOESNT_KNOW_SPELL, NOT_ENOUGH_MANA, ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL,
-		SECOND_HEROS_SPELL_IMMUNITY, SPELL_LEVEL_LIMIT_EXCEEDED, NO_SPELLS_TO_DISPEL
+		SECOND_HEROS_SPELL_IMMUNITY, SPELL_LEVEL_LIMIT_EXCEEDED, NO_SPELLS_TO_DISPEL,
+		NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET
 	};
 }
 

+ 108 - 25
lib/BattleState.cpp

@@ -665,8 +665,7 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
 				attackedCres.insert(st);
 		}
 	}
-	else if(VLC->spellh->spells[s->id]->attributes.find("CREATURE_TARGET_1") != std::string::npos
-		|| VLC->spellh->spells[s->id]->attributes.find("CREATURE_TARGET_2") != std::string::npos) //spell to be cast on a specific creature but massive on expert
+	else if(s->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
 	{
 		if(skillLevel < 3)  /*not expert */
 		{
@@ -679,8 +678,8 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
 			for(int it=0; it<stacks.size(); ++it)
 			{
 				/*if it's non negative spell and our unit or non positive spell and hostile unit */
-				if((VLC->spellh->spells[s->id]->positiveness >= 0 && stacks[it]->owner == attackerOwner)
-					||(VLC->spellh->spells[s->id]->positiveness <= 0 && stacks[it]->owner != attackerOwner )
+				if((s->positiveness >= 0 && stacks[it]->owner == attackerOwner)
+					||(s->positiveness <= 0 && stacks[it]->owner != attackerOwner )
 					)
 				{
 					if(!onlyAlive || stacks[it]->alive())
@@ -689,7 +688,7 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
 			}
 		} //if(caster->getSpellSchoolLevel(s) < 3)
 	}
-	else if(VLC->spellh->spells[s->id]->attributes.find("CREATURE_TARGET") != std::string::npos) //spell to be cast on one specific creature
+	else if(s->getTargetType() == CSpell::CREATURE)
 	{
 		CStack * st = getStackT(destinationTile, onlyAlive);
 		if(st)
@@ -1169,18 +1168,18 @@ const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack) const
 	return heroes[!stack->attackerOwned];
 }
 
-si8 BattleInfo::battleMaxSpellLevel() const
+si8 BattleInfo::battleMinSpellLevel() const
 {
-	si8 levelLimit = SPELL_LEVELS;
+	si8 levelLimit = 0;
 
 	if(const CGHeroInstance *h1 =  heroes[0])
 	{
-		amin(levelLimit, h1->valOfBonuses(Bonus::BLOCK_SPELLS_ABOVE_LEVEL));
+		amax(levelLimit, h1->valOfBonuses(Bonus::LEVEL_SPELL_IMMUNITY));
 	}
 
 	if(const CGHeroInstance *h2 = heroes[1])
 	{
-		amin(levelLimit, h2->valOfBonuses(Bonus::BLOCK_SPELLS_ABOVE_LEVEL));
+		amax(levelLimit, h2->valOfBonuses(Bonus::LEVEL_SPELL_IMMUNITY));
 	}
 
 	return levelLimit;
@@ -1461,8 +1460,8 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 	}
 
 	//spell level limiting bonus
-	curB->addNewBonus(new Bonus(Bonus::ONE_BATTLE, Bonus::BLOCK_SPELLS_ABOVE_LEVEL, Bonus::OTHER,
-		SPELL_LEVELS, -1, -1, Bonus::INDEPENDENT_MIN));;
+	curB->addNewBonus(new Bonus(Bonus::ONE_BATTLE, Bonus::LEVEL_SPELL_IMMUNITY, Bonus::OTHER,
+		0, -1, -1, Bonus::INDEPENDENT_MAX));
 
 	//giving terrain overalay premies
 	int bonusSubtype = -1;
@@ -1515,8 +1514,8 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 		{
 			curB->addNewBonus(makeFeature(Bonus::NO_MORALE, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY));
 			curB->addNewBonus(makeFeature(Bonus::NO_LUCK, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY));
-			Bonus * b = makeFeature(Bonus::BLOCK_SPELLS_ABOVE_LEVEL, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_OVERLAY);
-			b->valType = Bonus::INDEPENDENT_MIN;
+			Bonus * b = makeFeature(Bonus::LEVEL_SPELL_IMMUNITY, Bonus::ONE_BATTLE, SPELL_LEVELS, 1, Bonus::TERRAIN_OVERLAY);
+			b->valType = Bonus::INDEPENDENT_MAX;
 			curB->addNewBonus(b);
 			break;
 		}
@@ -1630,7 +1629,7 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player,
 	if(NBonus::hasOfType(heroes[1-cside], Bonus::SPELL_IMMUNITY, spell->id)) //non - casting hero provides immunity for this spell 
 		return SpellCasting::SECOND_HEROS_SPELL_IMMUNITY;
 
-	if(battleMaxSpellLevel() < spell->level) //non - casting hero stops caster from casting this spell
+	if(battleMinSpellLevel() > spell->level) //non - casting hero stops caster from casting this spell
 		return SpellCasting::SPELL_LEVEL_LIMIT_EXCEEDED;
 
 	int spellIDs[] = {66, 67, 68, 69}; //IDs of summon elemental spells (fire, earth, water, air)
@@ -1650,6 +1649,58 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player,
 		}
 	}
 
+	//checking if there exists an appropriate target
+	switch(spell->getTargetType())
+	{
+	case CSpell::CREATURE:
+	case CSpell::CREATURE_EXPERT_MASSIVE:
+		if(mode == HERO_CASTING)
+		{
+			const CGHeroInstance * caster = getHero(player);
+			bool targetExists = false;
+			BOOST_FOREACH(const CStack * stack, stacks)
+			{
+				switch (spell->positiveness)
+				{
+				case 1:
+					if(stack->owner == caster->getOwner())
+					{
+						if(canCastHereLower(player, spell, mode, stack->position) == SpellCasting::OK)
+						{
+							targetExists = true;
+							break;
+						}
+					}
+					break;
+				case 0:
+					if(canCastHereLower(player, spell, mode, stack->position) == SpellCasting::OK)
+					{
+						targetExists = true;
+						break;
+					}
+					break;
+				case -1:
+					if(stack->owner != caster->getOwner())
+					{
+						if(canCastHereLower(player, spell, mode, stack->position) == SpellCasting::OK)
+						{
+							targetExists = true;
+							break;
+						}
+					}
+					break;
+				}
+			}
+			if(!targetExists)
+			{
+				return SpellCasting::NO_APPROPRIATE_TARGET;
+			}
+		}
+		break;
+	case CSpell::OBSTACLE:
+		break;
+	}
+
 	return SpellCasting::OK;
 }
 
@@ -1659,25 +1710,56 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpellHere( int play
 	if(moreGeneralProblem != SpellCasting::OK)
 		return moreGeneralProblem;
 
+	return canCastHereLower(player, spell, mode, dest);
+}
+
+const CGHeroInstance * BattleInfo::getHero( int player ) const
+{
+	if(heroes[0] && heroes[0]->getOwner() == player)
+		return heroes[0];
+
+	return heroes[1];
+}
+
+SpellCasting::ESpellCastProblem BattleInfo::canCastHereLower( int player, const CSpell * spell, ECastingMode mode, THex dest ) const
+{
 	const CStack * subject = getStackT(dest, false);
-	//dispel helpful spells
-	if(spell->id == 78)
+	const CGHeroInstance * caster = mode == HERO_CASTING ? getHero(player) : NULL;
+	if(subject)
 	{
-		BonusList spellBon = subject->getSpellBonuses();
-		bool hasPositiveSpell = false;
-		BOOST_FOREACH(const Bonus * b, spellBon)
+		if(subject->hasBonusOfType(Bonus::SPELL_IMMUNITY, spell->id)
+			|| ( subject->hasBonusOfType(Bonus::LEVEL_SPELL_IMMUNITY) && subject->valOfBonuses(Bonus::LEVEL_SPELL_IMMUNITY) >= spell->level))
+		{
+			return SpellCasting::STACK_IMMUNE_TO_SPELL;
+		}
+		//dispel helpful spells
+		if(spell->id == 78)
 		{
-			if(VLC->spellh->spells[b->sid]->positiveness > 0)
+			BonusList spellBon = subject->getSpellBonuses();
+			bool hasPositiveSpell = false;
+			BOOST_FOREACH(const Bonus * b, spellBon)
 			{
-				hasPositiveSpell = true;
-				break;
+				if(VLC->spellh->spells[b->sid]->positiveness > 0)
+				{
+					hasPositiveSpell = true;
+					break;
+				}
+			}
+			if(!hasPositiveSpell)
+			{
+				return SpellCasting::NO_SPELLS_TO_DISPEL;
 			}
 		}
-		if(!hasPositiveSpell)
+	}
+	else
+	{
+		if(spell->getTargetType() == CSpell::CREATURE ||
+			(spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE && caster && caster->getSpellSchoolLevel(spell) < 3))
 		{
-			return SpellCasting::NO_SPELLS_TO_DISPEL;
+			return SpellCasting::WRONG_SPELL_TARGET;
 		}
 	}
+
 	return SpellCasting::OK;
 }
 
@@ -1833,7 +1915,8 @@ void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
 	 	sf.back().sid = sse.sid;
 	 	break;
 	case 34: //anti-magic
-	 	sf.push_back(featureGenerator(Bonus::LEVEL_SPELL_IMMUNITY, 0, power - 1, sse.turnsRemain));
+	 	sf.push_back(featureGenerator(Bonus::LEVEL_SPELL_IMMUNITY, SPELL_LEVELS, power - 1, sse.turnsRemain));
+		sf.back().valType = Bonus::INDEPENDENT_MAX;
 	 	sf.back().sid = sse.sid;
 	 	break;
 	case 41: //bless

+ 5 - 1
lib/BattleState.h

@@ -114,17 +114,21 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	si8 hasWallPenalty(const CStack * stack, THex destHex) const; //determines if given stack has wall penalty shooting given pos
 	si8 canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const; //determines if given stack can teleport to given place
 	bool battleCanShoot(const CStack * stack, THex dest) const; //determines if stack with given ID shoot at the selected destination
+	const CGHeroInstance * getHero(int player) const; //returns fighting hero that belongs to given player
 
 	enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING};
 
 	SpellCasting::ESpellCastProblem battleCanCastSpell(int player, ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
 	SpellCasting::ESpellCastProblem battleCanCastThisSpell(int player, const CSpell * spell, ECastingMode mode) const; //checks if given player can cast given spell
+private:
+	SpellCasting::ESpellCastProblem canCastHereLower(int player, const CSpell * spell, ECastingMode mode, THex dest) const; //same as battleCanCastThisSpellHere, but doesn't refer to battleCanCastThisSpell
+public:
 	SpellCasting::ESpellCastProblem battleCanCastThisSpellHere(int player, const CSpell * spell, ECastingMode mode, THex dest); //checks if given player can cast given spell at given tile in given mode
 
 	bool battleCanFlee(int player) const; //returns true if player can flee from the battle
 	const CStack * battleGetStack(THex pos, bool onlyAlive); //returns stack at given tile
 	const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none
-	si8 battleMaxSpellLevel() const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
+	si8 battleMinSpellLevel() const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
 	void localInit();
 	static BattleInfo * setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
 	bool isInTacticRange( THex dest ) const;

+ 3 - 3
lib/CArtHandler.cpp

@@ -627,7 +627,7 @@ void CArtHandler::addBonuses()
 	giveArtBonus(81,Bonus::FIRE_SPELL_DMG_PREMY,+50);//Orb of Tempestuous Fire
 	giveArtBonus(82,Bonus::WATER_SPELL_DMG_PREMY,+50);//Orb of Driving Rain
 
-	giveArtBonus(83,Bonus::BLOCK_SPELLS_ABOVE_LEVEL,3,-1,Bonus::INDEPENDENT_MIN);//Recanter's Cloak
+	giveArtBonus(83,Bonus::LEVEL_SPELL_IMMUNITY,3,-1,Bonus::INDEPENDENT_MAX);//Recanter's Cloak
 	giveArtBonus(84,Bonus::BLOCK_MORALE,0);//Spirit of Oppression
 	giveArtBonus(85,Bonus::BLOCK_LUCK,0);//Hourglass of the Evil Hour
 
@@ -684,7 +684,7 @@ void CArtHandler::addBonuses()
 
 	giveArtBonus(124,Bonus::SPELLS_OF_LEVEL,3,1); //Spellbinder's Hat
 	giveArtBonus(125,Bonus::ENEMY_CANT_ESCAPE,0); //Shackles of War
-	giveArtBonus(126,Bonus::BLOCK_SPELLS_ABOVE_LEVEL,0,-1,Bonus::INDEPENDENT_MIN);//Orb of Inhibition
+	giveArtBonus(126,Bonus::LEVEL_SPELL_IMMUNITY,SPELL_LEVELS,-1,Bonus::INDEPENDENT_MAX);//Orb of Inhibition
 
 	//vial of dragon blood
 	giveArtBonus(127, Bonus::PRIMARY_SKILL, +5, PrimarySkill::ATTACK, Bonus::BASE_NUMBER, new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE));
@@ -718,7 +718,7 @@ void CArtHandler::addBonuses()
 	giveArtBonus(133, Bonus::CREATURE_GROWTH_PERCENT, 50, -1);
 
 	//Power of the Dragon Father
-	giveArtBonus(134, Bonus::LEVEL_SPELL_IMMUNITY, 4);
+	giveArtBonus(134, Bonus::LEVEL_SPELL_IMMUNITY, 4, -1, Bonus::INDEPENDENT_MAX);
 
 	//Titan's Thunder
 	// FIXME: should also add a permanent spell book, somehow.

+ 15 - 0
lib/CSpellHandler.cpp

@@ -188,6 +188,21 @@ std::set<ui16> CSpell::rangeInHexes(unsigned int centralHex, ui8 schoolLvl ) con
 	return ret;
 }
 
+CSpell::ETargetType CSpell::getTargetType() const
+{
+	if(attributes.find("CREATURE_TARGET_1") != std::string::npos
+		|| attributes.find("CREATURE_TARGET_2") != std::string::npos)
+		return CREATURE_EXPERT_MASSIVE;
+
+	if(attributes.find("CREATURE_TARGET") != std::string::npos)
+		return CREATURE;
+
+	if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
+		return OBSTACLE;
+	
+	return NO_TARGET;
+}
+
 static bool startsWithX(const std::string &s)
 {
 	return s.size() && s[0] == 'x';

+ 2 - 0
lib/CSpellHandler.h

@@ -20,6 +20,7 @@
 class DLL_EXPORT CSpell
 {
 public:
+	enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE};
 	ui32 id;
 	std::string name;
 	std::string abbName; //abbreviated name
@@ -41,6 +42,7 @@ public:
 	std::vector<std::string> range; //description of spell's range in SRSL by magic school level
 	std::set<ui16> rangeInHexes(unsigned int centralHex, ui8 schoolLvl ) const; //convert range to specific hexes
 	si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none)
+	ETargetType getTargetType() const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 4 - 4
lib/HeroBonus.cpp

@@ -52,7 +52,7 @@ int DLL_EXPORT BonusList::totalValue() const
 			additive += i->val;
 			break;
 		case Bonus::INDEPENDENT_MAX:
-			if (!indepMax)
+			if (!hasIndepMax)
 			{
 				indepMax = i->val;
 				hasIndepMax = true;
@@ -64,14 +64,14 @@ int DLL_EXPORT BonusList::totalValue() const
 
 			break;
 		case Bonus::INDEPENDENT_MIN:
-			if (!indepMin)
+			if (!hasIndepMin)
 			{
 				indepMin = i->val;
 				hasIndepMin = true;
 			}
 			else
 			{
-				amax(indepMin, i->val);
+				amin(indepMin, i->val);
 			}
 
 			break;
@@ -85,7 +85,7 @@ int DLL_EXPORT BonusList::totalValue() const
 		assert(indepMin < indepMax);
 	if (hasIndepMax)
 		amax(valFirst, indepMax);
-	if (hasIndepMax)
+	if (hasIndepMin)
 		amin(valFirst, indepMin);
 
 	return valFirst;

+ 0 - 1
lib/HeroBonus.h

@@ -55,7 +55,6 @@ namespace PrimarySkill
 	BONUS_NAME(EARTH_SPELL_DMG_PREMY) \
 	BONUS_NAME(FIRE_SPELL_DMG_PREMY) \
 	BONUS_NAME(WATER_SPELL_DMG_PREMY) \
-	BONUS_NAME(BLOCK_SPELLS_ABOVE_LEVEL) \
 	BONUS_NAME(WATER_WALKING) /*subtype 1 - without penalty, 2 - with penalty*/ \
 	BONUS_NAME(NO_SHOTING_PENALTY) \
 	BONUS_NAME(NEGATE_ALL_NATURAL_IMMUNITIES) \