Ver código fonte

* fix for bug 573
* minor changes

mateuszb 14 anos atrás
pai
commit
4e631fc530
7 arquivos alterados com 97 adições e 39 exclusões
  1. 7 23
      CCallback.cpp
  2. 2 6
      CCallback.h
  3. 16 3
      client/CSpellWindow.cpp
  4. 10 0
      global.h
  5. 56 0
      lib/BattleState.cpp
  6. 3 0
      lib/BattleState.h
  7. 3 7
      server/CGameHandler.cpp

+ 7 - 23
CCallback.cpp

@@ -596,10 +596,7 @@ bool CBattleCallback::battleCanCastSpell()
 	if(!gs->curB) //there is no battle
 		return false;
 
-	if(gs->curB->sides[0] == player)
-		return gs->curB->castSpells[0] == 0 && gs->curB->heroes[0] && gs->curB->heroes[0]->getArt(17);
-	else
-		return gs->curB->castSpells[1] == 0 && gs->curB->heroes[1] && gs->curB->heroes[1]->getArt(17);
+	return gs->curB->battleCanCastSpell(player) == SpellCasting::OK;
 }
 
 bool CBattleCallback::battleCanFlee()
@@ -1091,29 +1088,16 @@ std::vector<int> CBattleCallback::battleGetDistances(const CStack * stack, THex
 	return ret;
 }
 
-CBattleCallback::ESpellCastProblem CBattleCallback::battleCanCastThisSpell( const CSpell * spell )
+SpellCasting::ESpellCastProblem CBattleCallback::battleCanCastThisSpell( const CSpell * spell )
 {
-	if(!battleCanCastSpell())
-		return GENERAL_CASTING_PROBLEM;
-
-	int spellIDs[] = {66, 67, 68, 69}; //IDs of summon elemental spells (fire, earth, water, air)
-	int creIDs[] = {114, 113, 115, 112}; //(fire, earth, water, air)
-
-	int * idp = std::find(spellIDs, spellIDs + ARRAY_COUNT(spellIDs), spell->id);
-	int arpos = idp - spellIDs;
-	if(arpos < ARRAY_COUNT(spellIDs))
+	if(!gs->curB)
 	{
-		//check if there are summoned elementals of other type
-		BOOST_FOREACH ( const CStack * st, gs->curB->stacks)
-		{
-			if (vstd::contains(st->state, SUMMONED) && st->getCreature()->idNumber != creIDs[arpos])
-			{
-				return ANOTHER_ELEMENTAL_SUMMONED;
-			}
-		}
+
+		tlog1 << "battleCanCastThisSpell called when there is no battle!\n";
+		return SpellCasting::NO_HERO_TO_CAST_SPELL;
 	}
 
-	return OK;
+	return gs->curB->battleCanCastThisSpell(player, spell);
 }
 
 si8 CBattleCallback::battleGetTacticDist()

+ 2 - 6
CCallback.h

@@ -75,10 +75,6 @@ class IBattleCallback
 {
 public:
 	///describes why player cannot cast a specific spell
-	enum ESpellCastProblem
-	{
-		OK, GENERAL_CASTING_PROBLEM, ANOTHER_ELEMENTAL_SUMMONED
-	};
 
 	enum EStackOwnership
 	{
@@ -100,7 +96,7 @@ public:
 	virtual std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, THex * predecessors = NULL)=0; //returns vector of distances to [dest hex number]
 	virtual bool battleCanShoot(const CStack * stack, THex dest)=0; //returns true if unit with id ID can shoot to dest
 	virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
-	virtual ESpellCastProblem battleCanCastThisSpell(const CSpell * spell)=0; //determines if given spell can be casted (and returns problem description)
+	virtual SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell)=0; //determines if given spell can be casted (and returns problem description)
 	virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle
 	virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead
 	virtual ui8 battleGetWallState(int partOfWall)=0; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
@@ -238,7 +234,7 @@ public:
 	std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, THex * predecessors = NULL) OVERRIDE; //returns vector of distances to [dest hex number]; if predecessors is not null, it must point to BFIELD_SIZE * sizeof(int) of allocated memory
 	bool battleCanShoot(const CStack * stack, THex dest) OVERRIDE; //returns true if unit with id ID can shoot to dest
 	bool battleCanCastSpell() OVERRIDE; //returns true, if caller can cast a spell
-	ESpellCastProblem battleCanCastThisSpell(const CSpell * spell) OVERRIDE; //determines if given spell can be casted (and returns problem description)
+	SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell) OVERRIDE; //determines if given spell can be casted (and returns problem description)
 	bool battleCanFlee() OVERRIDE; //returns true if caller can flee from the battle
 	const CGTownInstance * battleGetDefendedTown() OVERRIDE; //returns defended town if current battle is a siege, NULL instead
 	ui8 battleGetWallState(int partOfWall) OVERRIDE; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle

+ 16 - 3
client/CSpellWindow.cpp

@@ -17,8 +17,10 @@
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/foreach.hpp>
+#include <boost/format.hpp>
 #include "CBitmapHandler.h"
 #include "../lib/CHeroHandler.h"
+#include "../lib/BattleState.h"
 
 /*
  * CSpellWindow.cpp, part of VCMI engine
@@ -619,20 +621,29 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 		//we will cast a spell
 		if(sp->combatSpell && owner->myInt->battleInt && owner->myInt->cb->battleCanCastSpell()) //if battle window is open
 		{
-			IBattleCallback::ESpellCastProblem problem = owner->myInt->cb->battleCanCastThisSpell(sp);
+			SpellCasting::ESpellCastProblem problem = owner->myInt->cb->battleCanCastThisSpell(sp);
 			switch (problem)
 			{
-			case IBattleCallback::OK:
+			case SpellCasting::OK:
 				{
 					int spell = mySpell;
 					owner->fexitb();
 					owner->myInt->battleInt->castThisSpell(spell);
 					break;
 				}
-			case IBattleCallback::ANOTHER_ELEMENTAL_SUMMONED:
+			case SpellCasting::ANOTHER_ELEMENTAL_SUMMONED:
 				{
 					std::string text = CGI->generaltexth->allTexts[538], summoner, elemental, caster;
 					std::vector<const CStack *> stacks = owner->myInt->cb->battleGetStacks();
+					BOOST_FOREACH(const CStack * s, stacks)
+					{
+						if(vstd::contains(s->state, SUMMONED))
+						{
+							elemental = s->getCreature()->namePl;
+							summoner = owner->myInt->cb->battleGetFightingHero(!s->attackerOwned)->name;
+							break;
+						}
+					}
 					if (owner->myHero->type->sex)
 					{ //female
 						caster = CGI->generaltexth->allTexts[540];
@@ -641,6 +652,8 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 					{ //male
 						caster = CGI->generaltexth->allTexts[539];
 					}
+					text = boost::str(boost::format(text) % summoner % elemental % caster);
+
 
 					owner->myInt->showInfoDialog(text);
 				}

+ 10 - 0
global.h

@@ -307,6 +307,16 @@ enum EMarketMode
 	MARTKET_AFTER_LAST_PLACEHOLDER
 };
 
+namespace SpellCasting
+{
+	enum ESpellCastProblem
+	{
+		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
+	};
+}
+
 namespace Res
 {
 	enum ERes 

+ 56 - 0
lib/BattleState.cpp

@@ -1617,6 +1617,62 @@ bool BattleInfo::isInTacticRange( THex dest ) const
 		|| (tacticsSide && dest.getX() < BFIELD_WIDTH - 1 && dest.getX() >= BFIELD_WIDTH - tacticDistance - 1));
 }
 
+SpellCasting::ESpellCastProblem BattleInfo::battleCanCastSpell(int player) const
+{
+	int side = sides[0] == player ? 0 : 1;
+
+	if(castSpells[side] > 0)
+		return SpellCasting::ALREADY_CASTED_THIS_TURN;
+	if(!heroes[side])
+		return SpellCasting::NO_HERO_TO_CAST_SPELL;
+	if(!heroes[side]->getArt(17))
+		return SpellCasting::NO_SPELLBOOK;
+
+	return SpellCasting::OK;
+}
+
+SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player, const CSpell * spell ) const
+{
+	SpellCasting::ESpellCastProblem genProblem = battleCanCastSpell(player);
+	if(genProblem != SpellCasting::OK)
+		return genProblem;
+	int cside = sides[0] == player ? 0 : 1; //caster's side
+	const CGHeroInstance * caster = heroes[cside];
+	if(!caster->canCastThisSpell(spell))
+		return SpellCasting::HERO_DOESNT_KNOW_SPELL;
+
+	if(caster->mana < getSpellCost(spell, caster)) //not enough mana
+		return SpellCasting::NOT_ENOUGH_MANA;
+
+	if(spell->id < 10) //it's adventure spell (not combat))
+		return SpellCasting::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
+
+	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
+		return SpellCasting::SPELL_LEVEL_LIMIT_EXCEEDED;
+
+	int spellIDs[] = {66, 67, 68, 69}; //IDs of summon elemental spells (fire, earth, water, air)
+	int creIDs[] = {114, 113, 115, 112}; //(fire, earth, water, air)
+
+	int * idp = std::find(spellIDs, spellIDs + ARRAY_COUNT(spellIDs), spell->id);
+	int arpos = idp - spellIDs;
+	if(arpos < ARRAY_COUNT(spellIDs))
+	{
+		//check if there are summoned elementals of other type
+		BOOST_FOREACH ( const CStack * st, stacks)
+		{
+			if (vstd::contains(st->state, SUMMONED) && st->getCreature()->idNumber != creIDs[arpos])
+			{
+				return SpellCasting::ANOTHER_ELEMENTAL_SUMMONED;
+			}
+		}
+	}
+
+	return SpellCasting::OK;
+}
+
 CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
 	: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),   
 	counterAttacks(1)

+ 3 - 0
lib/BattleState.h

@@ -115,6 +115,9 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	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
 
+	SpellCasting::ESpellCastProblem battleCanCastSpell(int player) const; //returns true if there are no general issues preventing from castng a spell
+	SpellCasting::ESpellCastProblem battleCanCastThisSpell(int player, const CSpell * spell) const; //chcecks if given player can cast given spell
+
 	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

+ 3 - 7
server/CGameHandler.cpp

@@ -3824,15 +3824,11 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 			const CSpell *s = VLC->spellh->spells[ba.additionalInfo];
 			ui8 skill = h->getSpellSchoolLevel(s); //skill level
 
-			if(   !(h->canCastThisSpell(s)) //hero cannot cast this spell at all
-				|| (h->mana < gs->curB->getSpellCost(s, h)) //not enough mana
-				|| (ba.additionalInfo < 10) //it's adventure spell (not combat)
-				|| (gs->curB->castSpells[ba.side]) //spell has been cast
-				|| (NBonus::hasOfType(secondHero, Bonus::SPELL_IMMUNITY, s->id)) //non - casting hero provides immunity for this spell 
-				|| (gs->curB->battleMaxSpellLevel() < s->level) //non - casting hero stops caster from casting this spell
-				)
+			SpellCasting::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h->tempOwner, s);
+			if(escp != SpellCasting::OK)
 			{
 				tlog2 << "Spell cannot be cast!\n";
+				tlog2 << "Problem : " << escp << std::endl;
 				return false;
 			}