From 4e631fc5306c0506e5348bf41e2c79624827431b Mon Sep 17 00:00:00 2001 From: mateuszb Date: Tue, 15 Feb 2011 19:54:55 +0000 Subject: [PATCH] * fix for bug 573 * minor changes --- CCallback.cpp | 30 ++++++---------------- CCallback.h | 8 ++---- client/CSpellWindow.cpp | 19 +++++++++++--- global.h | 10 ++++++++ lib/BattleState.cpp | 56 +++++++++++++++++++++++++++++++++++++++++ lib/BattleState.h | 3 +++ server/CGameHandler.cpp | 10 +++----- 7 files changed, 97 insertions(+), 39 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index 2cd7be9f2..69da58f4e 100644 --- a/CCallback.cpp +++ b/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 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() diff --git a/CCallback.h b/CCallback.h index 2f2a81805..a2cbe5a22 100644 --- a/CCallback.h +++ b/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 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 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 diff --git a/client/CSpellWindow.cpp b/client/CSpellWindow.cpp index a638d3ba2..10d51f46f 100644 --- a/client/CSpellWindow.cpp +++ b/client/CSpellWindow.cpp @@ -17,8 +17,10 @@ #include #include #include +#include #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 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); } diff --git a/global.h b/global.h index 4ddad24ca..2e419e34e 100644 --- a/global.h +++ b/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 diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 4a78d7c85..95d2ff64a 100644 --- a/lib/BattleState.cpp +++ b/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) diff --git a/lib/BattleState.h b/lib/BattleState.h index 59ade847f..b9b93c4fc 100644 --- a/lib/BattleState.h +++ b/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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index a80d5f4be..918cc9d46 100644 --- a/server/CGameHandler.cpp +++ b/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; }