Sfoglia il codice sorgente

Fixed #293

Support for damage spell immunities.
DjWarmonger 14 anni fa
parent
commit
e5c11385cd
6 ha cambiato i file con 38 aggiunte e 49 eliminazioni
  1. 1 1
      client/CSpellWindow.cpp
  2. 1 1
      global.h
  3. 29 46
      lib/BattleState.cpp
  4. 1 0
      lib/BattleState.h
  5. 4 0
      lib/CSpellHandler.cpp
  6. 2 1
      lib/CSpellHandler.h

+ 1 - 1
client/CSpellWindow.cpp

@@ -772,7 +772,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 : NULL));
-		if(causedDmg == 0)
+		if(causedDmg == 0 || mySpell == 57) //Titan's Lightning Bolt already has damage info included
 			dmgInfo = "";
 		else
 		{

+ 1 - 1
global.h

@@ -25,7 +25,7 @@ typedef boost::int32_t si32; //signed int 32 bits (4 bytes)
 typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
 typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 typedef si64 expType;
-typedef ui16 spelltype;
+typedef ui32 TSpell;
 typedef std::pair<ui32, ui32> TDmgRange;
 typedef ui8 TBonusType;
 typedef si32 TBonusSubtype;

+ 29 - 46
lib/BattleState.cpp

@@ -914,13 +914,9 @@ ui32 BattleInfo::calculateSpellDmg( const CSpell * sp, const CGHeroInstance * ca
 
 	//15 - magic arrows, 16 - ice bolt, 17 - lightning bolt, 18 - implosion, 20 - frost ring, 21 - fireball, 22 - inferno, 23 - meteor shower,
 	//24 - death ripple, 25 - destroy undead, 26 - armageddon, 77 - thunderbolt
-	
-	//FIXME: what point of dmgMultipliers map? all damage multipliers are already present in CSpell::power
-	//TODO: better way to determine damage spells
-	static std::map <int, int> dmgMultipliers = boost::assign::map_list_of(15, 10)(16, 20)(17, 25)(18, 75)(20, 10)(21, 10)(22, 10)(23, 25)(24, 5)(25, 10)(26, 50)(77, 10);
 
 	//check if spell really does damage - if not, return 0
-	if(dmgMultipliers.find(sp->id) == dmgMultipliers.end())
+	if(VLC->spellh->damageSpells.find(sp->id) == VLC->spellh->damageSpells.end())
 		return 0;
 
 	ret = usedSpellPower * sp->power;
@@ -1782,6 +1778,21 @@ bool NegateRemover(const Bonus* b)
 	return b->source == Bonus::CREATURE_ABILITY;
 }
 
+bool BattleInfo::battleTestElementalImmunity(const CStack * subject, const CSpell * spell, Bonus::BonusType element, bool damageSpell) const //helper for battleisImmune
+{
+	if (spell->positiveness < 1) //negative or indifferent
+	{
+		if (damageSpell && subject->hasBonusOfType(element, 2) || subject->hasBonusOfType(element, 1))
+			return true;
+	}
+	else if (spell->positiveness == 1) //positive
+	{
+		if (subject->hasBonusOfType(element, 0)) //must be immune to all spells
+			return true;
+	}
+	return false;
+}
+
 SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, SpellCasting::ECastingMode mode, THex dest) const
 {
 	const CStack * subject = getStackT(dest, false);
@@ -1792,59 +1803,31 @@ SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInstance
 
 		if ((spell->id == 41 || spell->id == 42) && subject->hasBonusOfType(Bonus::UNDEAD)) //undeads are immune to bless & curse
 			return SpellCasting::STACK_IMMUNE_TO_SPELL; //TODO: more general logic for new spells?
+		
+		bool damageSpell = (VLC->spellh->damageSpells.find(spell->id) != VLC->spellh->damageSpells.end());
+
+		if (damageSpell && subject->hasBonusOfType(Bonus::DIRECT_DAMAGE_IMMUNITY));
+			return SpellCasting::STACK_IMMUNE_TO_SPELL;
 
 		if (spell->fire)
 		{
-
-			if (spell->positiveness == -1) //negative
-			{
-				if (subject->hasBonusOfType(Bonus::FIRE_IMMUNITY)) //both damage and curse spells, TODO: separate them
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
-			else if (spell->positiveness == 1)
-			{
-				if (subject->hasBonusOfType(Bonus::FIRE_IMMUNITY, 1))
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
+			if (battleTestElementalImmunity(subject, spell, Bonus::FIRE_IMMUNITY, damageSpell));
+				return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 		if (spell->water)
 		{
-			if (spell->positiveness == -1) //negative
-			{
-				if (subject->hasBonusOfType(Bonus::WATER_IMMUNITY)) //both damage and curse spells, TODO: separate them
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
-			else if (spell->positiveness == 1)
-			{
-				if (subject->hasBonusOfType(Bonus::WATER_IMMUNITY, 1))
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
+			if (battleTestElementalImmunity(subject, spell, Bonus::WATER_IMMUNITY, damageSpell));
+				return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 		if (spell->earth)
 		{
-			if (spell->positiveness == -1) //negative
-			{
-				if (subject->hasBonusOfType(Bonus::EARTH_IMMUNITY)) //both damage and curse spells, TODO: separate them
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
-			else if (spell->positiveness == 1)
-			{
-				if (subject->hasBonusOfType(Bonus::EARTH_IMMUNITY, 1))
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
+			if (battleTestElementalImmunity(subject, spell, Bonus::EARTH_IMMUNITY, damageSpell));
+				return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 		if (spell->air)
 		{
-			if (spell->positiveness == -1) //negative
-			{
-				if (subject->hasBonusOfType(Bonus::AIR_IMMUNITY)) //both damage and curse spells, TODO: separate them
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
-			else if (spell->positiveness == 1)
-			{
-				if (subject->hasBonusOfType(Bonus::AIR_IMMUNITY, 1))
-					return SpellCasting::STACK_IMMUNE_TO_SPELL;
-			}
+			if (battleTestElementalImmunity(subject, spell, Bonus::AIR_IMMUNITY, damageSpell));
+				return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 
 		BonusList immunities = subject->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));

+ 1 - 0
lib/BattleState.h

@@ -109,6 +109,7 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	SpellCasting::ESpellCastProblem battleCanCastThisSpell(int player, const CSpell * spell, SpellCasting::ECastingMode mode) const; //checks if given player can cast given spell
 	SpellCasting::ESpellCastProblem battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, SpellCasting::ECastingMode mode, THex dest) const; //checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
 	SpellCasting::ESpellCastProblem battleCanCastThisSpellHere(int player, const CSpell * spell, SpellCasting::ECastingMode mode, THex dest); //checks if given player can cast given spell at given tile in given mode
+	bool battleTestElementalImmunity(const CStack * subject, const CSpell * spell, Bonus::BonusType element, bool damageSpell) const;
 
 	std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<CStack*> affectedCreatures, int casterSideOwner, SpellCasting::ECastingMode mode) const;
 

+ 4 - 0
lib/CSpellHandler.cpp

@@ -4,6 +4,7 @@
 #include "CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include <boost/algorithm/string/replace.hpp>
+#include <boost/assign/std/set.hpp>
 #include <cctype>
 
 
@@ -18,6 +19,7 @@ extern CLodHandler *bitmaph;
  * Full text of license available in license.txt file, in main folder
  *
  */
+using namespace boost::assign;
 
 namespace SRSLPraserHelpers
 {
@@ -323,4 +325,6 @@ void CSpellHandler::loadSpells()
 	}
 	ast.close();
 	spells.push_back(spells[80]); //clone Acid Breath attributes for Acid Breath damage effect
+
+	damageSpells += 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 57, 77;
 }

+ 2 - 1
lib/CSpellHandler.h

@@ -63,11 +63,12 @@ class DLL_EXPORT CSpellHandler
 public:
 	CSpellHandler();
 	std::vector< ConstTransitivePtr<CSpell> > spells;
+	std::set<TSpell> damageSpells; //they inflict damage and require particular threatment
 	void loadSpells();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & spells;
+		h & spells & damageSpells;
 	}
 };