1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

* fix for bug 573

* minor changes
This commit is contained in:
mateuszb 2011-02-15 19:54:55 +00:00
parent 44619757e8
commit 4e631fc530
7 changed files with 97 additions and 39 deletions

View File

@ -596,10 +596,7 @@ bool CBattleCallback::battleCanCastSpell()
if(!gs->curB) //there is no battle if(!gs->curB) //there is no battle
return false; return false;
if(gs->curB->sides[0] == player) return gs->curB->battleCanCastSpell(player) == SpellCasting::OK;
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);
} }
bool CBattleCallback::battleCanFlee() bool CBattleCallback::battleCanFlee()
@ -1091,29 +1088,16 @@ std::vector<int> CBattleCallback::battleGetDistances(const CStack * stack, THex
return ret; return ret;
} }
CBattleCallback::ESpellCastProblem CBattleCallback::battleCanCastThisSpell( const CSpell * spell ) SpellCasting::ESpellCastProblem CBattleCallback::battleCanCastThisSpell( const CSpell * spell )
{ {
if(!battleCanCastSpell()) if(!gs->curB)
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))
{ {
//check if there are summoned elementals of other type
BOOST_FOREACH ( const CStack * st, gs->curB->stacks) tlog1 << "battleCanCastThisSpell called when there is no battle!\n";
{ return SpellCasting::NO_HERO_TO_CAST_SPELL;
if (vstd::contains(st->state, SUMMONED) && st->getCreature()->idNumber != creIDs[arpos])
{
return ANOTHER_ELEMENTAL_SUMMONED;
}
}
} }
return OK; return gs->curB->battleCanCastThisSpell(player, spell);
} }
si8 CBattleCallback::battleGetTacticDist() si8 CBattleCallback::battleGetTacticDist()

View File

@ -75,10 +75,6 @@ class IBattleCallback
{ {
public: public:
///describes why player cannot cast a specific spell ///describes why player cannot cast a specific spell
enum ESpellCastProblem
{
OK, GENERAL_CASTING_PROBLEM, ANOTHER_ELEMENTAL_SUMMONED
};
enum EStackOwnership 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 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 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 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 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 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 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 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 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 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 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 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 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

View File

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

View File

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

View File

@ -1617,6 +1617,62 @@ bool BattleInfo::isInTacticRange( THex dest ) const
|| (tacticsSide && dest.getX() < BFIELD_WIDTH - 1 && dest.getX() >= BFIELD_WIDTH - tacticDistance - 1)); || (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) CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), : base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
counterAttacks(1) counterAttacks(1)

View File

@ -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 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 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 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 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 const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none

View File

@ -3824,15 +3824,11 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
const CSpell *s = VLC->spellh->spells[ba.additionalInfo]; const CSpell *s = VLC->spellh->spells[ba.additionalInfo];
ui8 skill = h->getSpellSchoolLevel(s); //skill level ui8 skill = h->getSpellSchoolLevel(s); //skill level
if( !(h->canCastThisSpell(s)) //hero cannot cast this spell at all SpellCasting::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h->tempOwner, s);
|| (h->mana < gs->curB->getSpellCost(s, h)) //not enough mana if(escp != SpellCasting::OK)
|| (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
)
{ {
tlog2 << "Spell cannot be cast!\n"; tlog2 << "Spell cannot be cast!\n";
tlog2 << "Problem : " << escp << std::endl;
return false; return false;
} }