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:
commit
da4818eeb8
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user