1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Merge pull request #187 from vmarkovtsev/issue/2362

Fix 2362 cast spells in town garrison
This commit is contained in:
ArseniyShestakov 2016-02-02 14:49:54 +03:00
commit da4818eeb8
2 changed files with 66 additions and 60 deletions

View File

@ -6,6 +6,7 @@
#include "CAdvmapInterface.h"
#include "GUIClasses.h"
#include "InfoWindows.h"
#include "CCastleInterface.h"
#include "../CBitmapHandler.h"
#include "../CDefHandler.h"
@ -109,7 +110,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
Uint8 *sitesPerOurTab = s.combatSpell ? sitesPerTabBattle : sitesPerTabAdv;
++sitesPerOurTab[4];
s.forEachSchool([&sitesPerOurTab](const SpellSchoolInfo & school, bool & stop)
{
++sitesPerOurTab[(ui8)school.id];
@ -156,9 +157,9 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
leftCorner = BitmapHandler::loadBitmap("SpelTrnL.bmp", true);
rightCorner = BitmapHandler::loadBitmap("SpelTrnR.bmp", true);
spells = new CAnimation("Spells.def");
spellTab = CDefHandler::giveDef("SpelTab.def");
schools = CDefHandler::giveDef("Schools.def");
schoolBorders[0] = CDefHandler::giveDef("SplevA.def");
@ -380,9 +381,9 @@ public:
if(A.level<B.level)
return true;
if(A.level>B.level)
return false;
return false;
for(ui8 schoolId = 0; schoolId < 4; schoolId++)
{
if(A.school.at((ESpellSchool)schoolId) && !B.school.at((ESpellSchool)schoolId))
@ -390,7 +391,7 @@ public:
if(!A.school.at((ESpellSchool)schoolId) && B.school.at((ESpellSchool)schoolId))
return false;
}
return A.name < B.name;
}
} spellsorter;
@ -400,8 +401,8 @@ void CSpellWindow::computeSpellsPerArea()
std::vector<SpellID> spellsCurSite;
for(const SpellID & spellID : mySpells)
{
CSpell * s = spellID.toSpell();
CSpell * s = spellID.toSpell();
if(s->combatSpell ^ !battleSpellsOnly
&& ((selectedTab == 4) || (s->school[(ESpellSchool)selectedTab]))
)
@ -533,7 +534,7 @@ void CSpellWindow::keyPressed(const SDL_KeyboardEvent & key)
{
fexitb();
return;
}
}
if(key.state == SDL_PRESSED)
{
@ -622,10 +623,9 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
owner->myInt->showInfoDialog(std::string(msgBuf));
return;
}
//battle spell on adv map or adventure map spell during combat => display infowindow, not cast
if((sp->isCombatSpell() && !owner->myInt->battleInt)
|| (sp->isAdventureSpell() && owner->myInt->battleInt))
|| (sp->isAdventureSpell() && (owner->myInt->battleInt || owner->myInt->castleInt)))
{
std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell, 0));
LOCPLINT->showInfoDialog(sp->getLevelInfo(schoolLevel).description, hlp);
@ -705,26 +705,26 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
{
const CGHeroInstance *h = owner->myHero;
GH.popInt(owner);
auto guard = vstd::makeScopeGuard([this]
auto guard = vstd::makeScopeGuard([this]
{
(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastTabBattle : owner->myInt->spellbookSettings.spellbookLastTabAdvmap) = owner->selectedTab;
(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastPageBattle : owner->myInt->spellbookSettings.spellbokLastPageAdvmap) = owner->currentPage;
(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastPageBattle : owner->myInt->spellbookSettings.spellbokLastPageAdvmap) = owner->currentPage;
delete owner;
});
});
if(mySpell == SpellID::TOWN_PORTAL)
{
//special case
//todo: move to mechanics
std::vector <int> availableTowns;
std::vector <const CGTownInstance*> Towns = LOCPLINT->cb->getTownsInfo(false);
vstd::erase_if(Towns, [this](const CGTownInstance * t)
{
const auto relations = owner->myInt->cb->getPlayerRelations(t->tempOwner, owner->myInt->playerID);
return relations == PlayerRelations::ENEMIES;
const auto relations = owner->myInt->cb->getPlayerRelations(t->tempOwner, owner->myInt->playerID);
return relations == PlayerRelations::ENEMIES;
});
if (Towns.empty())
@ -776,13 +776,13 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
availableTowns.push_back(t->id.getNum());//add to the list
}
}
auto castTownPortal = [h](int townId)
{
const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(townId));
LOCPLINT->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
LOCPLINT->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
};
if (availableTowns.empty())
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
else
@ -791,13 +791,13 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41],
castTownPortal));
}
return;
return;
}
if(mySpell == SpellID::SUMMON_BOAT)
{
//special case
//todo: move to mechanics
//todo: move to mechanics
int3 pos = h->bestLocation();
if(pos.x < 0)
{
@ -805,10 +805,10 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
return;
}
}
if(sp->getTargetType() == CSpell::LOCATION)
{
adventureInt->enterCastingMode(sp);
adventureInt->enterCastingMode(sp);
}
else if(sp->getTargetType() == CSpell::NO_TARGET)
{
@ -817,7 +817,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
else
{
logGlobal->error("Invalid spell target type");
}
}
}
}
}
@ -865,11 +865,11 @@ void CSpellWindow::SpellArea::showAll(SDL_Surface * to)
if(mySpell < 0)
return;
const CSpell * spell = mySpell.toSpell();
const CSpell * spell = mySpell.toSpell();
owner->spells->load(mySpell);
IImage * icon = owner->spells->getImage(mySpell,0,false);
if(icon != nullptr)
icon->draw(to, pos.x, pos.y);
else

View File

@ -154,6 +154,13 @@ bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, Adve
}
const CGHeroInstance * caster = parameters.caster;
if(caster->inTownGarrison)
{
env->complain("Attempt to cast an adventure spell in town garrison");
return false;
}
const int cost = caster->getSpellCost(owner);
if(!caster->canCastThisSpell(owner))
@ -174,7 +181,7 @@ bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, Adve
asc.spellID = owner->id;
env->sendAndApply(&asc);
}
switch(applyAdventureEffects(env, parameters))
{
case ESpellCastResult::OK:
@ -184,7 +191,7 @@ bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, Adve
sm.absolute = false;
sm.val = -cost;
env->sendAndApply(&sm);
return true;
return true;
}
break;
case ESpellCastResult::CANCEL:
@ -224,16 +231,16 @@ ESpellCastResult DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnv
void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
{
logGlobal->debugStream() << "Started spell cast. Spell: "<<owner->name<<"; mode:"<<parameters.mode;
if(nullptr == parameters.caster)
{
env->complain("No spell-caster provided.");
return;
return;
}
BattleSpellCast sc;
prepareBattleCast(parameters, sc);
//check it there is opponent hero
const ui8 otherSide = 1-parameters.casterSide;
const CGHeroInstance * otherHero = nullptr;
@ -247,7 +254,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
spellCost = parameters.cb->battleGetSpellCost(owner, parameters.casterHero);
if(nullptr != otherHero) //handle mana channel
{
{
int manaChannel = 0;
for(const CStack * stack : parameters.cb->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow?
{
@ -271,10 +278,10 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks";
std::vector <const CStack*> reflected;//for magic mirror
//checking if creatures resist
//checking if creatures resist
handleResistance(env, attackedCres, sc);
//it is actual spell and can be reflected to single target, no recurrence
const bool tryMagicMirror = owner->isNegative() && owner->level && owner->getLevelInfo(0).range == "0";
const bool tryMagicMirror = owner->isNegative() && owner->level && owner->getLevelInfo(0).range == "0";
if(tryMagicMirror)
{
for(auto s : attackedCres)
@ -295,7 +302,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
effect.effect = 3;
effect.stack = s->ID;
sc.customEffects.push_back(effect);
}
}
}
for(auto cre : attackedCres)
@ -376,13 +383,13 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
}
}
void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const
{
const std::string attackedName = attackedStack->getName();
const std::string attackedNameSing = attackedStack->getCreature()->nameSing;
const std::string attackedNamePl = attackedStack->getCreature()->namePl;
auto getPluralFormat = [attackedStack](const int baseTextID) -> boost::format
{
return boost::format(VLC->generaltexth->allTexts[(attackedStack->count > 1 ? baseTextID + 1 : baseTextID)]);
@ -470,7 +477,7 @@ void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & log
logLines.push_back(text.str());
}
break;
}
}
}
void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
@ -479,7 +486,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
if(owner->isOffensiveSpell())
{
int spellDamage = parameters.effectValue;
int chainLightningModifier = 0;
for(auto & attackedCre : ctx.attackedCres)
{
@ -522,13 +529,13 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
vstd::amax(maxDuration, b.turnsRemain);
sse.effect.push_back(b);
}
//if all spell effects have special duration, use it
duration = maxDuration;
//if all spell effects have special duration, use it
duration = maxDuration;
}
//fix to original config: shield should display damage reduction
if(owner->id == SpellID::SHIELD || owner->id == SpellID::AIR_SHIELD)
{
sse.effect.back().val = (100 - sse.effect.back().val);
sse.effect.back().val = (100 - sse.effect.back().val);
}
//we need to know who cast Bind
if(owner->id == SpellID::BIND && parameters.casterStack)
@ -593,7 +600,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
if(!sse.stacks.empty())
env->sendAndApply(&sse);
}
}
}
std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
@ -738,7 +745,7 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
{
//no problems by default, this method is for spell-specific problems
//no problems by default, this method is for spell-specific problems
return ESpellCastProblem::OK;
}
@ -756,7 +763,7 @@ void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast
if(sourceSpell != nullptr)
{
//Special case: DISRUPTING_RAY is "immune" to dispell
//Other even PERMANENT effects can be removed (f.e. BIND)
//Other even PERMANENT effects can be removed (f.e. BIND)
if(sourceSpell->id == SpellID::DISRUPTING_RAY)
return false;
}
@ -766,7 +773,7 @@ void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast
{
CStack *s = battle->getStack(stackID);
s->popBonuses(CSelector(localSelector).And(selector));
}
}
}
void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, BattleSpellCastParameters& parameters) const
@ -781,12 +788,12 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat
if(!destination.isValid())
{
env->complain("MagicMirror: invalid destination");
return;
return;
}
BattleSpellCast sc;
prepareBattleCast(parameters, sc);
//calculating affected creatures for all spells
//must be vector, as in Chain Lightning order matters
std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
@ -802,14 +809,14 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat
{
sc.affectedCres.insert(cre->ID);
}
StacksInjured si;
SpellCastContext ctx(attackedCres, sc, si);
applyBattleEffects(env, parameters, ctx);
env->sendAndApply(&sc);
if(!si.stacks.empty()) //after spellcast info shows
env->sendAndApply(&si);
env->sendAndApply(&si);
logGlobal->debugStream() << "Finished spell cast. Spell: "<<owner->name<<"; mode: MAGIC_MIRROR";
}
@ -842,8 +849,8 @@ void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, s
effect.effect = 78;
effect.stack = s->ID;
sc.customEffects.push_back(effect);
}
}
}
}
}
void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& parameters, BattleSpellCast& sc) const
@ -855,6 +862,5 @@ void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& p
sc.dmgToDisplay = 0;
sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING;
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
sc.manaGained = 0;
sc.manaGained = 0;
}