From d4d16931525cdcc5d526e8823a87ff9a910d8768 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 07:10:42 +0300 Subject: [PATCH 01/60] Start with a todo note for this branch --- client/battle/CBattleInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index f90ea56ab..cc486c89d 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -2142,6 +2142,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) legalAction = true; bool hexesOutsideBattlefield = false; + //todo: move to spell mechanics auto tilesThatMustBeClear = sp->rangeInHexes(myNumber, hero->getSpellSchoolLevel(sp), side, &hexesOutsideBattlefield); for(BattleHex hex : tilesThatMustBeClear) { From f6715ad7870eaeca9ae1b7f2655300e95949cd3f Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 07:32:02 +0300 Subject: [PATCH 02/60] Use ISpellCaster in CSpell::canBeCast. * -2 todo notes --- lib/CBattleCallback.cpp | 2 +- lib/spells/BattleSpellMechanics.cpp | 21 ++++++++------------- lib/spells/BattleSpellMechanics.h | 6 +++--- lib/spells/CDefaultSpellMechanics.cpp | 2 +- lib/spells/CDefaultSpellMechanics.h | 2 +- lib/spells/CSpellHandler.cpp | 4 ++-- lib/spells/CSpellHandler.h | 2 +- lib/spells/ISpellMechanics.h | 2 +- 8 files changed, 18 insertions(+), 23 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index f6b885f4d..a34e72784 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1661,7 +1661,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell if(!spell->combatSpell) return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL; - const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, player); + const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, caster); if(specificProblem != ESpellCastProblem::OK) return specificProblem; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 1958d3654..c4dfcdbf6 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -326,7 +326,7 @@ void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, c env->sendAndApply(&ca); } -ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const +ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const { if(nullptr == cb->battleGetDefendedTown()) { @@ -338,11 +338,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl return ESpellCastProblem::NO_APPROPRIATE_TARGET; } - CSpell::TargetInfo ti(owner, 0);//TODO: use real spell level + CSpell::TargetInfo ti(owner, caster->getSpellSchoolLevel(owner)); if(ti.smart) { //if spell targeting is smart, then only attacker can use it - if(cb->playerToSide(player) != 0) + if(cb->playerToSide(caster->getOwner()) != 0) return ESpellCastProblem::NO_APPROPRIATE_TARGET; } @@ -526,18 +526,13 @@ HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectL } ///SacrificeMechanics -ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const +ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const { // for sacrifice we have to check for 2 targets (one dead to resurrect and one living to destroy) bool targetExists = false; bool targetToSacrificeExists = false; - const CGHeroInstance * caster = nullptr; //todo: use ISpellCaster - - if(cb->battleHasHero(cb->playerToSide(player))) - caster = cb->battleGetFightingHero(cb->playerToSide(player)); - for(const CStack * stack : cb->battleGetAllStacks()) { //using isImmuneBy directly as this mechanics does not have overridden immunity check @@ -545,7 +540,7 @@ ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattle //TODO: check that we really should check immunity for both stacks ESpellCastProblem::ESpellCastProblem res = owner->internalIsImmune(caster, stack); const bool immune = ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res; - const bool casterStack = stack->owner == player; + const bool casterStack = stack->owner == caster->getOwner(); if(!immune && casterStack) { @@ -635,13 +630,13 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac } ///SummonMechanics -ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const +ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const { //check if there are summoned elementals of other type - auto otherSummoned = cb->battleGetStacksIf([player, this](const CStack * st) + auto otherSummoned = cb->battleGetStacksIf([caster, this](const CStack * st) { - return (st->owner == player) + return (st->owner == caster->getOwner()) && (vstd::contains(st->state, EBattleStackState::SUMMONED)) && (!vstd::contains(st->state, EBattleStackState::CLONED)) && (st->getCreature()->idNumber != creatureToSummon); diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 8ac4399a3..383ca47b9 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -78,7 +78,7 @@ class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics { public: EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; @@ -127,7 +127,7 @@ class DLL_LINKAGE SacrificeMechanics : public RisingSpellMechanics public: SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -146,7 +146,7 @@ class DLL_LINKAGE SummonMechanics : public DefaultSpellMechanics public: SummonMechanics(CSpell * s, CreatureID cre): DefaultSpellMechanics(s), creatureToSummon(cre){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; private: diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index d0906623a..31f6be218 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -741,7 +741,7 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting return attackedCres; } -ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const +ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const { //no problems by default, this method is for spell-specific problems return ESpellCastProblem::OK; diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 910d25fd9..d426da32d 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -40,7 +40,7 @@ public: std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; std::set getAffectedStacks(SpellTargetingContext & ctx) const override; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index d847a676a..b7a073be3 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -143,9 +143,9 @@ ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affecte return adjustRawDamage(caster, affectedCreature, calculateRawEffectValue(spellSchoolLevel, usedSpellPower)); } -ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const +ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const { - return mechanics->canBeCast(cb, player); + return mechanics->canBeCast(cb, caster); } std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 01d798f9c..a99428ffe 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -267,7 +267,7 @@ public: ///internal interface (for callbacks) ///Checks general but spell-specific problems for all casting modes. Use only during battle. - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const; ///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc. ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index c48d0aebd..1028411ff 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -106,7 +106,7 @@ public: virtual std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0; virtual std::set getAffectedStacks(SpellTargetingContext & ctx) const = 0; - virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const = 0; + virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const = 0; virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const = 0; From d4a35c6839714f27568729a463fd1915d6e3f4aa Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 08:08:22 +0300 Subject: [PATCH 03/60] Simplify getAffectedStacks arguments. casterColor not needed anymore. --- AI/BattleAI/BattleAI.cpp | 2 +- lib/spells/CDefaultSpellMechanics.cpp | 10 +++++----- lib/spells/CSpellHandler.cpp | 4 ++-- lib/spells/CSpellHandler.h | 2 +- lib/spells/ISpellMechanics.h | 6 +++--- server/CGameHandler.cpp | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 753375b1b..9017fb5c3 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -458,7 +458,7 @@ void CBattleAI::attemptCastingSpell() { int damageDealt = 0, damageReceived = 0; - auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, playerID, skillLevel, ps.dest, hero); + auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, hero, skillLevel, ps.dest); if(stacksSuffering.empty()) return -1; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 31f6be218..99335bac2 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -272,7 +272,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS //must be vector, as in Chain Lightning order matters std::vector attackedCres; //CStack vector is somewhat more suitable than ID vector - auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.getFirstDestinationHex(), parameters.caster); + auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres)); logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks"; @@ -672,7 +672,7 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting { std::set attackedCres;//std::set to exclude multiple occurrences of two hex creatures - const ui8 attackerSide = ctx.cb->playerToSide(ctx.casterColor) == 1; + const ui8 attackerSide = ctx.cb->playerToSide(ctx.caster->getOwner()) == 1; const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide); const CSpell::TargetInfo ti(owner, ctx.schoolLvl, ctx.mode); @@ -691,8 +691,8 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting else if(ti.type == CSpell::CREATURE) { auto predicate = [=](const CStack * s){ - const bool positiveToAlly = owner->isPositive() && s->owner == ctx.casterColor; - const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.casterColor; + const bool positiveToAlly = owner->isPositive() && s->owner == ctx.caster->getOwner(); + const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.caster->getOwner(); const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class //for single target spells select stacks covering destination tile @@ -796,7 +796,7 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat //must be vector, as in Chain Lightning order matters std::vector attackedCres; //CStack vector is somewhat more suitable than ID vector - auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, destination, parameters.caster); + auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, destination); std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres)); logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks"; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index b7a073be3..1acf58239 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -153,9 +153,9 @@ std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes); } -std::set CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const ISpellCaster * caster) const +std::set CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const { - ISpellMechanics::SpellTargetingContext ctx(this, cb,mode,casterColor,spellLvl,destination); + ISpellMechanics::SpellTargetingContext ctx(this, cb, mode, caster, spellLvl, destination); std::set attackedCres = mechanics->getAffectedStacks(ctx); diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index a99428ffe..77fb75851 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -218,7 +218,7 @@ public: ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; ///selects from allStacks actually affected stacks - std::set getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const ISpellCaster * caster) const; + std::set getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const; si32 getCost(const int skillLevel) const; diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 1028411ff..a9f07a4b2 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -91,11 +91,11 @@ public: CSpell::TargetInfo ti; ECastingMode::ECastingMode mode; BattleHex destination; - PlayerColor casterColor; + const ISpellCaster * caster; int schoolLvl; - SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode m, PlayerColor cc, int lvl, BattleHex dest) - : cb(c), ti(s,lvl, m), mode(m), destination(dest), casterColor(cc), schoolLvl(lvl) + SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode m, const ISpellCaster * caster_, int lvl, BattleHex dest) + : cb(c), ti(s,lvl, m), mode(m), destination(dest), caster(caster_), schoolLvl(lvl) {}; }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 6538e3eb3..8d78c29d6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -842,7 +842,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt //TODO: should spell override creature`s projectile? - std::set attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att->owner, bonus->val, targetHex, att); + std::set attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att, bonus->val, targetHex); //TODO: get exact attacked hex for defender From 8d2aa2c8c78fc1487c2cb6089dd7219f01f4701a Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 08:16:02 +0300 Subject: [PATCH 04/60] Reduced scope of CBattleInterface spellSelMode --- client/battle/CBattleInterface.cpp | 4 ++-- client/battle/CBattleInterface.h | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index cc486c89d..97ff133c8 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -98,7 +98,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe std::shared_ptr att, std::shared_ptr defen) : background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0), activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1), - currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(nullptr), sp(nullptr), + currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellToCast(nullptr), sp(nullptr), siegeH(nullptr), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0), givenCommand(nullptr), myTurn(false), resWindow(nullptr), moveStarted(false), moveSoundHander(-1), bresult(nullptr) { @@ -1385,7 +1385,7 @@ void CBattleInterface::castThisSpell(SpellID spellID) const CGHeroInstance * castingHero = (attackingHeroInstance->tempOwner == curInt->playerID) ? attackingHeroInstance : defendingHeroInstance; assert(castingHero); // code below assumes non-null hero sp = spellID.toSpell(); - spellSelMode = ANY_LOCATION; + PossibleActions spellSelMode = ANY_LOCATION; const CSpell::TargetInfo ti(sp, castingHero->getSpellSchoolLevel(sp)); diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index dbf33913c..363a2895e 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -158,7 +158,6 @@ private: bool stackCanCastSpell; //if true, active stack could possibly cast some target spell bool creatureCasting; //if true, stack currently aims to cats a spell bool spellDestSelectMode; //if true, player is choosing destination for his spell - only for GUI / console - PossibleActions spellSelMode; BattleAction * spellToCast; //spell for which player is choosing destination const CSpell * sp; //spell pointer for convenience si32 creatureSpellToCast; @@ -194,7 +193,7 @@ private: const CBattleInterface * owner; public: const CGTownInstance * town; //besieged town - + SiegeHelper(const CGTownInstance * siegeTown, const CBattleInterface * _owner); //c-tor ~SiegeHelper(); //d-tor @@ -335,13 +334,13 @@ public: void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook void displayEffect(ui32 effect, int destTile, bool areaEffect = true); //displays custom effect on the battlefield - + void displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s cast animation void displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation void displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation - + void displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect = true); - + void battleTriggerEffect(const BattleTriggerEffect & bte); void setBattleCursor(const int myNumber); //really complex and messy, sets attackingHex void endAction(const BattleAction* action); @@ -365,7 +364,7 @@ public: friend class CPlayerInterface; friend class CButton; friend class CInGameConsole; - + friend class CBattleResultWindow; friend class CBattleHero; friend class CSpellEffectAnimation; From 3b2a45c8dcafcc452877aa5f220ada5675a70995 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 08:49:29 +0300 Subject: [PATCH 05/60] Get rid of battleGetPossibleTargets. --- AI/BattleAI/BattleAI.cpp | 54 ++++++++++++++++++++++++++++++---------- AI/BattleAI/BattleAI.h | 16 ++++++------ lib/CBattleCallback.cpp | 44 -------------------------------- lib/CBattleCallback.h | 1 - 4 files changed, 49 insertions(+), 66 deletions(-) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 9017fb5c3..9bfe246c7 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -430,7 +430,7 @@ void CBattleAI::attemptCastingSpell() std::vector possibleCasts; for(auto spell : possibleSpells) { - for(auto hex : getTargetsToConsider(spell)) + for(auto hex : getTargetsToConsider(spell, hero)) { PossibleSpellcast ps = {spell, hex}; possibleCasts.push_back(ps); @@ -527,24 +527,52 @@ void CBattleAI::attemptCastingSpell() cb->battleMakeAction(&spellcast); } -std::vector CBattleAI::getTargetsToConsider( const CSpell *spell ) const +std::vector CBattleAI::getTargetsToConsider(const CSpell * spell, const ISpellCaster * caster) const { - if(spell->getTargetType() == CSpell::NO_TARGET) + const CSpell::TargetInfo targetInfo(spell, caster->getSpellSchoolLevel(spell)); + std::vector ret; + + if(targetInfo.massive || targetInfo.type == CSpell::NO_TARGET) { - //Spell can be cast anywhere, all hexes are potentially considerable. - std::vector ret; - - for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) - if(BattleHex(i).isAvailable()) - ret.push_back(i); - - return ret; + ret.push_back(BattleHex()); } else { - //TODO when massive effect -> doesn't matter where cast - return cbc->battleGetPossibleTargets(playerID, spell); + switch(targetInfo.type) + { + case CSpell::CREATURE: + { + for(const CStack * stack : cbc->battleAliveStacks()) + { + bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack); + bool casterStack = stack->owner == caster->getOwner(); + + if(!immune) + switch (spell->positiveness) + { + case CSpell::POSITIVE: + if(casterStack || targetInfo.smart) + ret.push_back(stack->position); + break; + + case CSpell::NEUTRAL: + ret.push_back(stack->position); + break; + + case CSpell::NEGATIVE: + if(!casterStack || targetInfo.smart) + ret.push_back(stack->position); + break; + } + } + } + break; + default: + break; + } } + + return ret; } boost::optional CBattleAI::considerFleeingOrSurrendering() diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index b6612145d..e3cc42cbb 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -51,11 +51,11 @@ static bool willSecondHexBlockMoreEnemyShooters(const BattleHex &h1, const Battl struct ThreatMap -{ +{ std::array, GameConstants::BFIELD_SIZE> threatMap; // [hexNr] -> enemies able to strike - - const CStack *endangered; - std::array sufferedDamage; + + const CStack *endangered; + std::array sufferedDamage; ThreatMap(const CStack *Endangered); }; @@ -89,7 +89,7 @@ const Val getValOr(const std::map &Map, const Key &key, const Val2 def auto i = Map.find(key); if(i != Map.end()) return i->second; - else + else return defaultValue; } @@ -111,8 +111,8 @@ class CBattleAI : public CBattleGameInterface { int side; std::shared_ptr cb; - - //Previous setting of cb + + //Previous setting of cb bool wasWaitingForRealize, wasUnlockingGs; void print(const std::string &text) const; @@ -148,6 +148,6 @@ public: boost::optional considerFleeingOrSurrendering(); void attemptCastingSpell(); - std::vector getTargetsToConsider(const CSpell *spell) const; + std::vector getTargetsToConsider(const CSpell *spell, const ISpellCaster * caster) const; }; diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index a34e72784..3b011ffd9 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1739,50 +1739,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell return ESpellCastProblem::OK; } -std::vector CBattleInfoCallback::battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const -{ - std::vector ret; - RETURN_IF_NOT_BATTLE(ret); - - switch(spell->getTargetType()) - { - case CSpell::CREATURE: - { - const CGHeroInstance * caster = battleGetFightingHero(playerToSide(player)); //TODO - const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell)); - - for(const CStack * stack : battleAliveStacks()) - { - bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack); - bool casterStack = stack->owner == caster->getOwner(); - - if(!immune) - switch (spell->positiveness) - { - case CSpell::POSITIVE: - if(casterStack || ti.smart) - ret.push_back(stack->position); - break; - - case CSpell::NEUTRAL: - ret.push_back(stack->position); - break; - - case CSpell::NEGATIVE: - if(!casterStack || ti.smart) - ret.push_back(stack->position); - break; - } - } - } - break; - default: - logGlobal->errorStream() << "FIXME " << __FUNCTION__ << " doesn't work with target type " << spell->getTargetType(); - } - - return ret; -} - ui32 CBattleInfoCallback::battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const { RETURN_IF_NOT_BATTLE(-1); diff --git a/lib/CBattleCallback.h b/lib/CBattleCallback.h index fdf7be1df..dccbb695d 100644 --- a/lib/CBattleCallback.h +++ b/lib/CBattleCallback.h @@ -284,7 +284,6 @@ public: ESpellCastProblem::ESpellCastProblem battleCanCastSpell(PlayerColor player, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode) const; //checks if given player can cast given spell ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode - std::vector battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const; SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const; SpellID getRandomBeneficialSpell(const CStack * subject) const; From 3de47d4df6c873981e4fc24c4e74dfb607c8ee63 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 09:07:51 +0300 Subject: [PATCH 06/60] [AI] Teach BattleAI how to use offensive location spells (like fireball) * AI already can evaluate effect of smart and not smart offensive spells. --- AI/BattleAI/BattleAI.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 9bfe246c7..0bd58c54c 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -567,6 +567,14 @@ std::vector CBattleAI::getTargetsToConsider(const CSpell * spell, con } } break; + case CSpell::LOCATION: + { + for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) + if(BattleHex(i).isAvailable()) + ret.push_back(i); + } + break; + default: break; } From 750c114648271b0bd23dadac5a066467e99ac08f Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 09:50:04 +0300 Subject: [PATCH 07/60] Drafts --- client/battle/CBattleInterface.h | 3 ++- lib/spells/CDefaultSpellMechanics.cpp | 7 +++++++ lib/spells/CDefaultSpellMechanics.h | 1 + lib/spells/CSpellHandler.cpp | 11 +++++++++++ lib/spells/CSpellHandler.h | 3 +++ lib/spells/ISpellMechanics.h | 2 ++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index 363a2895e..6354294a3 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -116,7 +116,8 @@ class CBattleInterface : public CIntObject MOVE_STACK, ATTACK, WALK_AND_ATTACK, ATTACK_AND_RETURN, SHOOT, //OPEN_GATE, //we can open castle gate during siege NO_LOCATION, ANY_LOCATION, FRIENDLY_CREATURE_SPELL, HOSTILE_CREATURE_SPELL, RISING_SPELL, ANY_CREATURE, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL, FREE_LOCATION, //used with Force Field and Fire Wall - all tiles affected by spell must be free - CATAPULT, HEAL, RISE_DEMONS + CATAPULT, HEAL, RISE_DEMONS, + AIMED_SPELL }; private: SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 99335bac2..f86e2d803 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -747,6 +747,13 @@ ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBat return ESpellCastProblem::OK; } +ESpellCastProblem DefaultSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const +{ + //no problems by default, this method is for spell-specific problems + //common problems handled by CSpell + return ESpellCastProblem::OK; +} + ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const { //by default use general algorithm diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index d426da32d..d1d98dc7d 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -41,6 +41,7 @@ public: std::set getAffectedStacks(SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 1acf58239..8d0102786 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -305,6 +305,17 @@ void CSpell::getEffects(std::vector & lst, const int level) const } } +ESpellCastProblem::ESpellCastProblem CSpell::canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const +{ + + //todo: CSpell::canBeCastAt check common problems + + SpellTargetingContext ctx(this, cb, mode, caster, , destination); + + return mechanics->canBeCast(cb, caster, mode, destination); +} + + ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const { // Get all stacks at destination hex. only alive if not rising spell diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 77fb75851..713ccad58 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -270,6 +270,9 @@ public: ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const; ///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc. + ESpellCastProblem::ESpellCastProblem canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; + + ///checks for creature immunity *at given hex*. ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; ///checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc. diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index a9f07a4b2..393504862 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -108,6 +108,8 @@ public: virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const = 0; + virtual ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const = 0; + virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const = 0; virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0; From 40e6e0c1436594407465b537ea476f1b6ee76fba Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 11:55:09 +0300 Subject: [PATCH 08/60] Move REMOVE_OBSTACLE-related code from callback to where it should be. --- lib/CBattleCallback.cpp | 45 ++++----------------------- lib/spells/BattleSpellMechanics.cpp | 33 +++++++++++++++++++- lib/spells/BattleSpellMechanics.h | 1 + lib/spells/CDefaultSpellMechanics.cpp | 2 +- lib/spells/CSpellHandler.cpp | 5 ++- 5 files changed, 42 insertions(+), 44 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 3b011ffd9..e3016a09d 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1776,47 +1776,14 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell return ESpellCastProblem::INVALID; } const PlayerColor player = caster->getOwner(); - ESpellCastProblem::ESpellCastProblem moreGeneralProblem = battleCanCastThisSpell(caster, spell, mode); - if(moreGeneralProblem != ESpellCastProblem::OK) - return moreGeneralProblem; - if(spell->getTargetType() == CSpell::OBSTACLE) - { - if(spell->id == SpellID::REMOVE_OBSTACLE) - { - if(auto obstacle = battleGetObstacleOnPos(dest, false)) - { - switch (obstacle->obstacleType) - { - case CObstacleInstance::ABSOLUTE_OBSTACLE: //cliff-like obstacles can't be removed - case CObstacleInstance::MOAT: - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - case CObstacleInstance::USUAL: - return ESpellCastProblem::OK; - -// //TODO FIRE_WALL only for ADVANCED level casters -// case CObstacleInstance::FIRE_WALL: -// return -// //TODO other magic obstacles for EXPERT -// case CObstacleInstance::QUICKSAND: -// case CObstacleInstance::LAND_MINE: -// case CObstacleInstance::FORCE_FIELD: -// return - default: -// assert(0); - return ESpellCastProblem::OK; - } - } - } - //isObstacleOnTile(dest) - // - // - //TODO - //assert that it's remove obstacle - //rules whether we can remove spell-created obstacle - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } + ESpellCastProblem::ESpellCastProblem problem = battleCanCastThisSpell(caster, spell, mode); + if(problem != ESpellCastProblem::OK) + return problem; + problem = spell->canBeCastAt(this, caster, mode, dest); + if(problem != ESpellCastProblem::OK) + return problem; //get dead stack if we cast resurrection or animate dead const CStack *deadStack = getStackIf([dest](const CStack *s) { return !s->alive() && s->coversPos(dest); }); diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index c4dfcdbf6..004916d34 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -465,7 +465,6 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con } } - ///WallMechanics std::vector WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes) const { @@ -516,6 +515,38 @@ void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * en env->complain("There's no obstacle to remove!"); } +ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const SpellTargetingContext & ctx) const +{ + ESpellCastProblem::ESpellCastProblem res = ESpellCastProblem::NO_APPROPRIATE_TARGET; + + if(auto obstacle = ctx.cb->battleGetObstacleOnPos(ctx.destination, false)) + { + switch (obstacle->obstacleType) + { + case CObstacleInstance::ABSOLUTE_OBSTACLE: //cliff-like obstacles can't be removed + case CObstacleInstance::MOAT: + break; + case CObstacleInstance::USUAL: + res = ESpellCastProblem::OK; + break; + case CObstacleInstance::FIRE_WALL: + if(ctx.schoolLvl >= 2) + res = ESpellCastProblem::OK; + break; + case CObstacleInstance::QUICKSAND: + case CObstacleInstance::LAND_MINE: + case CObstacleInstance::FORCE_FIELD: + if(ctx.schoolLvl >= 3) + res = ESpellCastProblem::OK; + break; + default: + break; + } + } + return res; +} + +///RisingSpellMechanics HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const { //this may be even distinct class diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 383ca47b9..90505c99c 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -110,6 +110,7 @@ class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics { public: RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index f86e2d803..0821510fd 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -747,7 +747,7 @@ ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBat return ESpellCastProblem::OK; } -ESpellCastProblem DefaultSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const +ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const { //no problems by default, this method is for spell-specific problems //common problems handled by CSpell diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 8d0102786..56504b054 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -310,12 +310,11 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCastAt(const CBattleInfoCallba //todo: CSpell::canBeCastAt check common problems - SpellTargetingContext ctx(this, cb, mode, caster, , destination); + ISpellMechanics::SpellTargetingContext ctx(this, cb, mode, caster, caster->getSpellSchoolLevel(this), destination); - return mechanics->canBeCast(cb, caster, mode, destination); + return mechanics->canBeCast(ctx); } - ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const { // Get all stacks at destination hex. only alive if not rising spell From f05375465320de18e963a671347e2d5dbabc759b Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 12:20:38 +0300 Subject: [PATCH 09/60] Advance REMOVE_OBSTACLE mechanics --- lib/spells/BattleSpellMechanics.cpp | 75 ++++++++++++++++++----------- lib/spells/BattleSpellMechanics.h | 5 ++ 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 004916d34..9507850b8 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -507,43 +507,64 @@ void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * en { if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.getFirstDestinationHex(), false)) { - ObstaclesRemoved obr; - obr.obstacles.insert(obstacleToRemove->uniqueID); - env->sendAndApply(&obr); + if(canRemove(obstacleToRemove.get(), parameters.spellLvl)) + { + ObstaclesRemoved obr; + obr.obstacles.insert(obstacleToRemove->uniqueID); + env->sendAndApply(&obr); + } + else + { + env->complain("Cant remove this obstacle!"); + } } else env->complain("There's no obstacle to remove!"); } +ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const +{ + const int spellLevel = caster->getSpellSchoolLevel(owner); + + for(auto obstacle : cb->battleGetAllObstacles()) + if(canRemove(obstacle.get(), spellLevel)) + return ESpellCastProblem::OK; + + return ESpellCastProblem::NO_APPROPRIATE_TARGET; +} + ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const SpellTargetingContext & ctx) const { - ESpellCastProblem::ESpellCastProblem res = ESpellCastProblem::NO_APPROPRIATE_TARGET; - if(auto obstacle = ctx.cb->battleGetObstacleOnPos(ctx.destination, false)) + if(canRemove(obstacle.get(), ctx.schoolLvl)) + return ESpellCastProblem::OK; + + return ESpellCastProblem::NO_APPROPRIATE_TARGET; +} + +bool RemoveObstacleMechanics::canRemove(const CObstacleInstance * obstacle, const int spellLevel) const +{ + switch (obstacle->obstacleType) { - switch (obstacle->obstacleType) - { - case CObstacleInstance::ABSOLUTE_OBSTACLE: //cliff-like obstacles can't be removed - case CObstacleInstance::MOAT: - break; - case CObstacleInstance::USUAL: - res = ESpellCastProblem::OK; - break; - case CObstacleInstance::FIRE_WALL: - if(ctx.schoolLvl >= 2) - res = ESpellCastProblem::OK; - break; - case CObstacleInstance::QUICKSAND: - case CObstacleInstance::LAND_MINE: - case CObstacleInstance::FORCE_FIELD: - if(ctx.schoolLvl >= 3) - res = ESpellCastProblem::OK; - break; - default: - break; - } + case CObstacleInstance::ABSOLUTE_OBSTACLE: //cliff-like obstacles can't be removed + case CObstacleInstance::MOAT: + return false; + case CObstacleInstance::USUAL: + return true; + case CObstacleInstance::FIRE_WALL: + if(spellLevel >= 2) + return true; + break; + case CObstacleInstance::QUICKSAND: + case CObstacleInstance::LAND_MINE: + case CObstacleInstance::FORCE_FIELD: + if(spellLevel >= 3) + return true; + break; + default: + break; } - return res; + return false; } ///RisingSpellMechanics diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 90505c99c..16ff46014 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -12,6 +12,8 @@ #include "CDefaultSpellMechanics.h" +class CObstacleInstance; + class DLL_LINKAGE HealingSpellMechanics : public DefaultSpellMechanics { public: @@ -110,9 +112,12 @@ class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics { public: RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; +private: + bool canRemove(const CObstacleInstance * obstacle, const int spellLevel) const; }; ///all rising spells From be67faad90913b2b236646bef621e937c7935de3 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 12:44:54 +0300 Subject: [PATCH 10/60] Drafts --- lib/CBattleCallback.cpp | 2 -- lib/spells/BattleSpellMechanics.cpp | 6 ++++++ lib/spells/BattleSpellMechanics.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index e3016a09d..2e3b7c904 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1732,8 +1732,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell } } break; - case CSpell::OBSTACLE: - break; } return ESpellCastProblem::OK; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 9507850b8..e90822e35 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -568,6 +568,12 @@ bool RemoveObstacleMechanics::canRemove(const CObstacleInstance * obstacle, cons } ///RisingSpellMechanics +ESpellCastProblem::ESpellCastProblem RisingSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const +{ + //todo: RisingSpellMechanics::canBeCast + return ESpellCastProblem::OK; +} + HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const { //this may be even distinct class diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 16ff46014..1489d12e3 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -125,6 +125,7 @@ class DLL_LINKAGE RisingSpellMechanics : public HealingSpellMechanics { public: RisingSpellMechanics(CSpell * s): HealingSpellMechanics(s){}; + ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; EHealLevel getHealLevel(int effectLevel) const override; }; From 22d885af223fd18f18422028b9bcab07b108ff37 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 1 Mar 2016 15:14:27 +0300 Subject: [PATCH 11/60] Fixed http://bugs.vcmi.eu/view.php?id=2302 --- lib/spells/BattleSpellMechanics.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index e90822e35..5d930e310 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -346,6 +346,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl return ESpellCastProblem::NO_APPROPRIATE_TARGET; } + const auto attackableBattleHexes = cb->getAttackableBattleHexes(); + + if(attackableBattleHexes.empty()) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + return ESpellCastProblem::OK; } From b634482bb042c342ed895b1df5f3e99e57cc2d4d Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 2 Mar 2016 21:23:50 +0300 Subject: [PATCH 12/60] Fix http://bugs.vcmi.eu/view.php?id=2422 --- lib/spells/BattleSpellMechanics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 5d930e310..1fbf4e1ad 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -430,7 +430,7 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1) { BattleHex hex = i; - if(hex.getX() > 2 && hex.getX() < 14 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) + if(!(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) availableTiles.push_back(hex); } boost::range::random_shuffle(availableTiles); From 0d5eaa1183aaee3739051c62a4757111b949e3d4 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 3 Mar 2016 13:41:13 +0300 Subject: [PATCH 13/60] Proper fix for http://bugs.vcmi.eu/view.php?id=2422 --- lib/spells/BattleSpellMechanics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 1fbf4e1ad..0c9a27b1f 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -430,7 +430,7 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1) { BattleHex hex = i; - if(!(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) + if(hex.getX() > 0 && hex.getX() < 16 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) availableTiles.push_back(hex); } boost::range::random_shuffle(availableTiles); From b09f150e7b7176b493926a8a30e7a9b9f0597a35 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Fri, 4 Mar 2016 16:42:55 +0300 Subject: [PATCH 14/60] Factored out battleCanCastThisSpellHere --- lib/CBattleCallback.cpp | 28 +------------------------ lib/spells/BattleSpellMechanics.cpp | 21 +++++++++++++------ lib/spells/BattleSpellMechanics.h | 5 +++-- lib/spells/CSpellHandler.cpp | 32 +++++++++++++++++++++-------- lib/spells/CSpellHandler.h | 8 ++++---- 5 files changed, 46 insertions(+), 48 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 2e3b7c904..9fc98713e 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1773,38 +1773,12 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastThisSpellHere: no spellcaster."; return ESpellCastProblem::INVALID; } - const PlayerColor player = caster->getOwner(); ESpellCastProblem::ESpellCastProblem problem = battleCanCastThisSpell(caster, spell, mode); if(problem != ESpellCastProblem::OK) return problem; - problem = spell->canBeCastAt(this, caster, mode, dest); - if(problem != ESpellCastProblem::OK) - return problem; - - //get dead stack if we cast resurrection or animate dead - const CStack *deadStack = getStackIf([dest](const CStack *s) { return !s->alive() && s->coversPos(dest); }); - const CStack *aliveStack = getStackIf([dest](const CStack *s) { return s->alive() && s->coversPos(dest);}); - - - if(spell->isRisingSpell()) - { - if(!deadStack && !aliveStack) - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - if(deadStack && deadStack->owner != player) //you can resurrect only your own stacks //FIXME: it includes alive stacks as well - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } - else if(spell->getTargetType() == CSpell::CREATURE) - { - if(!aliveStack) - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - if(spell->isNegative() && aliveStack->owner == player) - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - if(spell->isPositive() && aliveStack->owner != player) - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } - return spell->isImmuneAt(this, caster, mode, dest); + return spell->canBeCastAt(this, caster, mode, dest); } const CStack * CBattleInfoCallback::getStackIf(std::function pred) const diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 0c9a27b1f..9dc1363ab 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -573,12 +573,6 @@ bool RemoveObstacleMechanics::canRemove(const CObstacleInstance * obstacle, cons } ///RisingSpellMechanics -ESpellCastProblem::ESpellCastProblem RisingSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const -{ - //todo: RisingSpellMechanics::canBeCast - return ESpellCastProblem::OK; -} - HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const { //this may be even distinct class @@ -673,6 +667,21 @@ int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const } ///SpecialRisingSpellMechanics +ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const +{ + const CStack * stack = ctx.cb->getStackIf([ctx](const CStack * s) + { + const bool ownerMatches = !ctx.ti.smart || s->getOwner() == ctx.caster->getOwner(); + + return ownerMatches && s->isValidTarget(true) && s->coversPos(ctx.destination); + }); + + if(nullptr == stack) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + + return ESpellCastProblem::OK; +} + ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const { // following does apply to resurrect and animate dead(?) only diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 1489d12e3..083f94a9b 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -125,7 +125,7 @@ class DLL_LINKAGE RisingSpellMechanics : public HealingSpellMechanics { public: RisingSpellMechanics(CSpell * s): HealingSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; + EHealLevel getHealLevel(int effectLevel) const override; }; @@ -140,11 +140,12 @@ protected: int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; -///all rising spells but SACRIFICE +///ANIMATE_DEAD and RESURRECTION class DLL_LINKAGE SpecialRisingSpellMechanics : public RisingSpellMechanics { public: SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){}; + ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; }; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 56504b054..f0f64f2b0 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -222,11 +222,6 @@ bool CSpell::isNeutral() const return positiveness == NEUTRAL; } -bool CSpell::isHealingSpell() const -{ - return isRisingSpell() || (id == SpellID::CURE); -} - bool CSpell::isRisingSpell() const { return isRising; @@ -307,12 +302,31 @@ void CSpell::getEffects(std::vector & lst, const int level) const ESpellCastProblem::ESpellCastProblem CSpell::canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const { - - //todo: CSpell::canBeCastAt check common problems - ISpellMechanics::SpellTargetingContext ctx(this, cb, mode, caster, caster->getSpellSchoolLevel(this), destination); - return mechanics->canBeCast(ctx); + + ESpellCastProblem::ESpellCastProblem specific = mechanics->canBeCast(ctx); + + if(specific != ESpellCastProblem::OK) + return specific; + + //todo: this should be moved to mechanics + if(ctx.ti.onlyAlive && ctx.ti.smart && getTargetType() == CSpell::CREATURE) + { + const CStack * aliveStack = cb->getStackIf([destination](const CStack * s) + { + return s->isValidTarget(false) && s->coversPos(destination); + }); + + if(!aliveStack) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + if(isNegative() && aliveStack->owner == caster->getOwner()) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + if(isPositive() && aliveStack->owner != caster->getOwner()) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + + return isImmuneAt(cb, caster, mode, destination); } ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 713ccad58..183113baf 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -204,7 +204,6 @@ public: bool isNeutral() const; bool isDamageSpell() const; - bool isHealingSpell() const; bool isRisingSpell() const; bool isOffensiveSpell() const; @@ -272,9 +271,6 @@ public: ///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc. ESpellCastProblem::ESpellCastProblem canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; - ///checks for creature immunity *at given hex*. - ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; - ///checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc. ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const; public: @@ -303,6 +299,10 @@ public://internal, for use only by Mechanics classes ESpellCastProblem::ESpellCastProblem internalIsImmune(const ISpellCaster * caster, const CStack *obj) const; private: + + ///checks for creature immunity *at given hex*. + ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; + void setIsOffensive(const bool val); void setIsRising(const bool val); From d1579ea62024f6ab1f3846f7808a86ad4444b0bb Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Fri, 4 Mar 2016 17:53:09 +0300 Subject: [PATCH 15/60] Simplify spell action selection for creature target. --- client/battle/CBattleInterface.cpp | 51 ++++-------------------------- client/battle/CBattleInterface.h | 6 ++-- lib/spells/CSpellHandler.cpp | 8 ++--- 3 files changed, 13 insertions(+), 52 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 97ff133c8..2dcf3e01b 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1397,10 +1397,7 @@ void CBattleInterface::castThisSpell(SpellID spellID) } else if(ti.type == CSpell::CREATURE) { - if(ti.smart) - spellSelMode = selectionTypeByPositiveness(*sp); - else - spellSelMode = ANY_CREATURE; + spellSelMode = AIMED_SPELL_CREATURE; } else if(ti.type == CSpell::OBSTACLE) { @@ -1645,7 +1642,7 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack) possibleActions.push_back (OBSTACLE); break; default: - possibleActions.push_back (selectionTypeByPositiveness (*spell)); + possibleActions.push_back (AIMED_SPELL_CREATURE); break; } @@ -1949,21 +1946,6 @@ void CBattleInterface::bTacticNextStack(const CStack *current /*= nullptr*/) } -CBattleInterface::PossibleActions CBattleInterface::selectionTypeByPositiveness(const CSpell & spell) -{ - switch(spell.positiveness) - { - case CSpell::NEGATIVE : - return HOSTILE_CREATURE_SPELL; - case CSpell::NEUTRAL: - return ANY_CREATURE; - case CSpell::POSITIVE: - return FRIENDLY_CREATURE_SPELL; - } - assert(0); - return NO_LOCATION; //should never happen -} - std::string formatDmgRange(std::pair dmgRange) { if(dmgRange.first != dmgRange.second) @@ -2076,27 +2058,10 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) legalAction = true; } break; - case ANY_CREATURE: - if (shere && shere->alive() && isCastingPossibleHere (sactive, shere, myNumber)) + case AIMED_SPELL_CREATURE: + if (shere && isCastingPossibleHere (sactive, shere, myNumber)) legalAction = true; break; - case HOSTILE_CREATURE_SPELL: - if (shere && shere->alive() && !ourStack && isCastingPossibleHere (sactive, shere, myNumber)) - legalAction = true; - break; - case FRIENDLY_CREATURE_SPELL: - { - if (isCastingPossibleHere (sactive, shere, myNumber)) //need to be called before sp is determined - { - bool rise = false; //TODO: can you imagine rising hostile creatures? - sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; - if (sp && sp->isRisingSpell()) - rise = true; - if (shere && (shere->alive() || rise) && ourStack) - legalAction = true; - } - break; - } case RANDOM_GENIE_SPELL: { if (shere && ourStack && shere != sactive) //only positive spells for other allied creatures @@ -2282,9 +2247,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) consoleMsg = (boost::format(CGI->generaltexth->allTexts[296]) % shere->getName() % sactive->shots % estDmgText).str(); } break; - case HOSTILE_CREATURE_SPELL: - case FRIENDLY_CREATURE_SPELL: - case ANY_CREATURE: + case AIMED_SPELL_CREATURE: sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s switch (sp->id) @@ -2358,9 +2321,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) { switch (illegalAction) { - case ANY_CREATURE: - case HOSTILE_CREATURE_SPELL: - case FRIENDLY_CREATURE_SPELL: + case AIMED_SPELL_CREATURE: case RANDOM_GENIE_SPELL: cursorFrame = ECursor::COMBAT_BLOCKED; consoleMsg = CGI->generaltexth->allTexts[23]; diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index 6354294a3..7b9473c21 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -114,10 +114,10 @@ class CBattleInterface : public CIntObject INVALID = -1, CREATURE_INFO, MOVE_TACTICS, CHOOSE_TACTICS_STACK, MOVE_STACK, ATTACK, WALK_AND_ATTACK, ATTACK_AND_RETURN, SHOOT, //OPEN_GATE, //we can open castle gate during siege - NO_LOCATION, ANY_LOCATION, FRIENDLY_CREATURE_SPELL, HOSTILE_CREATURE_SPELL, RISING_SPELL, ANY_CREATURE, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL, + NO_LOCATION, ANY_LOCATION, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL, FREE_LOCATION, //used with Force Field and Fire Wall - all tiles affected by spell must be free CATAPULT, HEAL, RISE_DEMONS, - AIMED_SPELL + AIMED_SPELL_CREATURE }; private: SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes; @@ -347,7 +347,7 @@ public: void endAction(const BattleAction* action); void hideQueue(); void showQueue(); - PossibleActions selectionTypeByPositiveness(const CSpell & spell); + Rect hexPosition(BattleHex hex) const; void handleHex(BattleHex myNumber, int eventType); diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index f0f64f2b0..8f8e82d2e 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -304,14 +304,14 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCastAt(const CBattleInfoCallba { ISpellMechanics::SpellTargetingContext ctx(this, cb, mode, caster, caster->getSpellSchoolLevel(this), destination); - ESpellCastProblem::ESpellCastProblem specific = mechanics->canBeCast(ctx); if(specific != ESpellCastProblem::OK) return specific; //todo: this should be moved to mechanics - if(ctx.ti.onlyAlive && ctx.ti.smart && getTargetType() == CSpell::CREATURE) + //rising spells handled by mechanics + if(ctx.ti.onlyAlive && getTargetType() == CSpell::CREATURE) { const CStack * aliveStack = cb->getStackIf([destination](const CStack * s) { @@ -320,9 +320,9 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCastAt(const CBattleInfoCallba if(!aliveStack) return ESpellCastProblem::NO_APPROPRIATE_TARGET; - if(isNegative() && aliveStack->owner == caster->getOwner()) + if(ctx.ti.smart && isNegative() && aliveStack->owner == caster->getOwner()) return ESpellCastProblem::NO_APPROPRIATE_TARGET; - if(isPositive() && aliveStack->owner != caster->getOwner()) + if(ctx.ti.smart && isPositive() && aliveStack->owner != caster->getOwner()) return ESpellCastProblem::NO_APPROPRIATE_TARGET; } From 5d329b40a4054b6aa2e600e2ea138815a922f202 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Fri, 4 Mar 2016 19:11:42 +0300 Subject: [PATCH 16/60] Factored spell action selection for location target. --- client/battle/CBattleInterface.cpp | 29 ++++------------------------- lib/spells/BattleSpellMechanics.cpp | 27 +++++++++++++++++++++++++++ lib/spells/BattleSpellMechanics.h | 2 +- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 2dcf3e01b..4a84ac176 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -2099,31 +2099,11 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) notLegal = true; break; case FREE_LOCATION: + legalAction = true; + if(!isCastingPossibleHere(sactive, shere, myNumber)) { - ui8 side = curInt->cb->battleGetMySide(); - auto hero = curInt->cb->battleGetMyHero(); - assert(!creatureCasting); //we assume hero casts this spell - assert(hero); - - legalAction = true; - bool hexesOutsideBattlefield = false; - //todo: move to spell mechanics - auto tilesThatMustBeClear = sp->rangeInHexes(myNumber, hero->getSpellSchoolLevel(sp), side, &hexesOutsideBattlefield); - for(BattleHex hex : tilesThatMustBeClear) - { - if(curInt->cb->battleGetStackByPos(hex, true) || !!curInt->cb->battleGetObstacleOnPos(hex, false) - || !hex.isAvailable()) - { - legalAction = false; - notLegal = true; - } - } - - if(hexesOutsideBattlefield) - { - legalAction = false; - notLegal = true; - } + legalAction = false; + notLegal = true; } break; case CATAPULT: @@ -2288,7 +2268,6 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) isCastingPossible = true; break; case FREE_LOCATION: - //cursorFrame = ECursor::SPELLBOOK; consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s isCastingPossible = true; break; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 9dc1363ab..7eb0ac34b 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -371,6 +371,33 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I } ///ObstacleMechanics +ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const SpellTargetingContext & ctx) const +{ + if(ctx.ti.clearAffected) + { + ui8 side = ctx.cb->playerToSide(ctx.caster->getOwner()); + + bool hexesOutsideBattlefield = false; + + auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.caster->getSpellSchoolLevel(owner), side, &hexesOutsideBattlefield); + + for(BattleHex hex : tilesThatMustBeClear) + { + if(ctx.cb->battleGetStackByPos(hex, true) || !!ctx.cb->battleGetObstacleOnPos(hex, false) || !hex.isAvailable()) + { + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + } + + if(hexesOutsideBattlefield) + { + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + } + + return ESpellCastProblem::OK; +} + void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { auto placeObstacle = [&, this](const BattleHex & pos) diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 083f94a9b..570dcafdc 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -96,7 +96,7 @@ class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics { public: ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; - + ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; From 1e7e7a700607a2cc48fc8b19e82d5382c4bcd4b3 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 5 Mar 2016 04:46:32 +0300 Subject: [PATCH 17/60] Fixed http://bugs.vcmi.eu/view.php?id=2419 --- lib/NetPacksLib.cpp | 9 +++++++++ lib/spells/BattleSpellMechanics.cpp | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 581a5a030..e33206ea4 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1198,6 +1198,15 @@ DLL_LINKAGE void BattleNextRound::applyGs( CGameState *gs ) s->counterAttacksTotalCache = 0; // new turn effects s->battleTurnPassed(); + + if(s->alive() && vstd::contains(s->state, EBattleStackState::CLONED)) + { + //cloned stack has special lifetime marker + //check it after bonuses updated in battleTurnPassed() + + if(!s->hasBonus(Selector::type(Bonus::NONE).And(Selector::source(Bonus::SPELL_EFFECT, SpellID::CLONE)))) + s->makeGhost(); + } } for(auto &obst : gs->curB->obstacles) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 7eb0ac34b..1cad38e81 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -131,6 +131,13 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, const ssp.val = bsa.newStackID; ssp.absolute = 1; env->sendAndApply(&ssp); + + SetStackEffect sse; + sse.stacks.push_back(bsa.newStackID); + Bonus lifeTimeMarker(Bonus::N_TURNS, Bonus::NONE, Bonus::SPELL_EFFECT, 0, owner->id.num); + lifeTimeMarker.turnsRemain = parameters.enchantPower; + sse.effect.push_back(lifeTimeMarker); + env->sendAndApply(&sse); } ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const From c0cf5d6956c6f903a48539b3d2dd379e9379a6a8 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 4 Sep 2016 04:59:30 +0300 Subject: [PATCH 18/60] Style tweaks --- lib/spells/BattleSpellMechanics.cpp | 2 +- lib/spells/CDefaultSpellMechanics.cpp | 16 +++++++--------- lib/spells/ISpellMechanics.h | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 1cad38e81..9a4074020 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -386,7 +386,7 @@ ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const SpellTar bool hexesOutsideBattlefield = false; - auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.caster->getSpellSchoolLevel(owner), side, &hexesOutsideBattlefield); + auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side, &hexesOutsideBattlefield); for(BattleHex hex : tilesThatMustBeClear) { diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 0821510fd..12ee47802 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -675,37 +675,35 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting const ui8 attackerSide = ctx.cb->playerToSide(ctx.caster->getOwner()) == 1; const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide); - const CSpell::TargetInfo ti(owner, ctx.schoolLvl, ctx.mode); - //TODO: more generic solution for mass spells if(owner->getLevelInfo(ctx.schoolLvl).range.size() > 1) //custom many-hex range { for(BattleHex hex : attackedHexes) { - if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ti.onlyAlive)) + if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) { attackedCres.insert(st); } } } - else if(ti.type == CSpell::CREATURE) + else if(ctx.ti.type == CSpell::CREATURE) { auto predicate = [=](const CStack * s){ const bool positiveToAlly = owner->isPositive() && s->owner == ctx.caster->getOwner(); const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.caster->getOwner(); - const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class + const bool validTarget = s->isValidTarget(!ctx.ti.onlyAlive); //todo: this should be handled by spell class //for single target spells select stacks covering destination tile - const bool rangeCovers = ti.massive || s->coversPos(ctx.destination); + const bool rangeCovers = ctx.ti.massive || s->coversPos(ctx.destination); //handle smart targeting - const bool positivenessFlag = !ti.smart || owner->isNeutral() || positiveToAlly || negativeToEnemy; + const bool positivenessFlag = !ctx.ti.smart || owner->isNeutral() || positiveToAlly || negativeToEnemy; return rangeCovers && positivenessFlag && validTarget; }; TStacks stacks = ctx.cb->battleGetStacksIf(predicate); - if(ti.massive) + if(ctx.ti.massive) { //for massive spells add all targets for (auto stack : stacks) @@ -733,7 +731,7 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting { for(BattleHex hex : attackedHexes) { - if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ti.onlyAlive)) + if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) attackedCres.insert(st); } } diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 393504862..2a01af53a 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -94,8 +94,8 @@ public: const ISpellCaster * caster; int schoolLvl; - SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode m, const ISpellCaster * caster_, int lvl, BattleHex dest) - : cb(c), ti(s,lvl, m), mode(m), destination(dest), caster(caster_), schoolLvl(lvl) + SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode mode_, const ISpellCaster * caster_, int schoolLvl_, BattleHex destination_) + : cb(c), ti(s,schoolLvl_, mode_), mode(mode_), destination(destination_), caster(caster_), schoolLvl(schoolLvl_) {}; }; From eb128a0207886b69758ae806ff9691baaf654fa1 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 4 Sep 2016 05:15:37 +0300 Subject: [PATCH 19/60] More style tweaks --- lib/spells/BattleSpellMechanics.cpp | 20 ++++++++-------- lib/spells/BattleSpellMechanics.h | 8 +++---- lib/spells/CDefaultSpellMechanics.cpp | 12 +++++----- lib/spells/CDefaultSpellMechanics.h | 4 ++-- lib/spells/CSpellHandler.cpp | 8 +++---- lib/spells/ISpellMechanics.h | 33 +++++++++++++-------------- 6 files changed, 42 insertions(+), 43 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 9a4074020..bea2480f3 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -62,12 +62,12 @@ void AntimagicMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast } ///ChainLightningMechanics -std::set ChainLightningMechanics::getAffectedStacks(SpellTargetingContext & ctx) const +std::set ChainLightningMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const { std::set attackedCres; std::set possibleHexes; - for(auto stack : ctx.cb->battleGetAllStacks()) + for(auto stack : cb->battleGetAllStacks()) { if(stack->isValidTarget()) { @@ -82,7 +82,7 @@ std::set ChainLightningMechanics::getAffectedStacks(SpellTargeti BattleHex lightningHex = ctx.destination; for(int i = 0; i < targetsOnLevel[ctx.schoolLvl]; ++i) { - auto stack = ctx.cb->battleGetStackByPos(lightningHex, true); + auto stack = cb->battleGetStackByPos(lightningHex, true); if(!stack) break; attackedCres.insert (stack); @@ -378,11 +378,11 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I } ///ObstacleMechanics -ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const SpellTargetingContext & ctx) const +ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { if(ctx.ti.clearAffected) { - ui8 side = ctx.cb->playerToSide(ctx.caster->getOwner()); + ui8 side = cb->playerToSide(ctx.caster->getOwner()); bool hexesOutsideBattlefield = false; @@ -390,7 +390,7 @@ ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const SpellTar for(BattleHex hex : tilesThatMustBeClear) { - if(ctx.cb->battleGetStackByPos(hex, true) || !!ctx.cb->battleGetObstacleOnPos(hex, false) || !hex.isAvailable()) + if(cb->battleGetStackByPos(hex, true) || !!cb->battleGetObstacleOnPos(hex, false) || !hex.isAvailable()) { return ESpellCastProblem::NO_APPROPRIATE_TARGET; } @@ -572,9 +572,9 @@ ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CB return ESpellCastProblem::NO_APPROPRIATE_TARGET; } -ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const SpellTargetingContext & ctx) const +ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { - if(auto obstacle = ctx.cb->battleGetObstacleOnPos(ctx.destination, false)) + if(auto obstacle = cb->battleGetObstacleOnPos(ctx.destination, false)) if(canRemove(obstacle.get(), ctx.schoolLvl)) return ESpellCastProblem::OK; @@ -701,9 +701,9 @@ int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const } ///SpecialRisingSpellMechanics -ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const +ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { - const CStack * stack = ctx.cb->getStackIf([ctx](const CStack * s) + const CStack * stack = cb->getStackIf([ctx](const CStack * s) { const bool ownerMatches = !ctx.ti.smart || s->getOwner() == ctx.caster->getOwner(); diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 570dcafdc..9339dc383 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -43,7 +43,7 @@ class DLL_LINKAGE ChainLightningMechanics : public DefaultSpellMechanics { public: ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){}; - std::set getAffectedStacks(SpellTargetingContext & ctx) const override; + std::set getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; }; class DLL_LINKAGE CloneMechanics : public DefaultSpellMechanics @@ -96,7 +96,7 @@ class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics { public: ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; @@ -113,7 +113,7 @@ class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics public: RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; - ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; private: @@ -145,7 +145,7 @@ class DLL_LINKAGE SpecialRisingSpellMechanics : public RisingSpellMechanics { public: SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; }; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 12ee47802..c043e88e3 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -668,11 +668,11 @@ std::vector DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, return ret; } -std::set DefaultSpellMechanics::getAffectedStacks(SpellTargetingContext & ctx) const +std::set DefaultSpellMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const { std::set attackedCres;//std::set to exclude multiple occurrences of two hex creatures - const ui8 attackerSide = ctx.cb->playerToSide(ctx.caster->getOwner()) == 1; + const ui8 attackerSide = cb->playerToSide(ctx.caster->getOwner()) == 1; const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide); //TODO: more generic solution for mass spells @@ -680,7 +680,7 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting { for(BattleHex hex : attackedHexes) { - if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) + if(const CStack * st = cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) { attackedCres.insert(st); } @@ -701,7 +701,7 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting return rangeCovers && positivenessFlag && validTarget; }; - TStacks stacks = ctx.cb->battleGetStacksIf(predicate); + TStacks stacks = cb->battleGetStacksIf(predicate); if(ctx.ti.massive) { @@ -731,7 +731,7 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting { for(BattleHex hex : attackedHexes) { - if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) + if(const CStack * st = cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) attackedCres.insert(st); } } @@ -745,7 +745,7 @@ ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBat return ESpellCastProblem::OK; } -ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const SpellTargetingContext & ctx) const +ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { //no problems by default, this method is for spell-specific problems //common problems handled by CSpell diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index d1d98dc7d..db020eb66 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -38,10 +38,10 @@ public: DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; - std::set getAffectedStacks(SpellTargetingContext & ctx) const override; + std::set getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; - ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 8f8e82d2e..9a758a978 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -155,9 +155,9 @@ std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, std::set CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const { - ISpellMechanics::SpellTargetingContext ctx(this, cb, mode, caster, spellLvl, destination); + SpellTargetingContext ctx(this, mode, caster, spellLvl, destination); - std::set attackedCres = mechanics->getAffectedStacks(ctx); + std::set attackedCres = mechanics->getAffectedStacks(cb, ctx); //now handle immunities auto predicate = [&, this](const CStack * s)->bool @@ -302,9 +302,9 @@ void CSpell::getEffects(std::vector & lst, const int level) const ESpellCastProblem::ESpellCastProblem CSpell::canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const { - ISpellMechanics::SpellTargetingContext ctx(this, cb, mode, caster, caster->getSpellSchoolLevel(this), destination); + SpellTargetingContext ctx(this, mode, caster, caster->getSpellSchoolLevel(this), destination); - ESpellCastProblem::ESpellCastProblem specific = mechanics->canBeCast(ctx); + ESpellCastProblem::ESpellCastProblem specific = mechanics->canBeCast(cb, ctx); if(specific != ESpellCastProblem::OK) return specific; diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 2a01af53a..1fb343546 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -82,33 +82,32 @@ struct DLL_LINKAGE AdventureSpellCastParameters int3 pos; }; +struct DLL_LINKAGE SpellTargetingContext +{ + CSpell::TargetInfo ti; + ECastingMode::ECastingMode mode; + BattleHex destination; + const ISpellCaster * caster; + int schoolLvl; + + SpellTargetingContext(const CSpell * s, ECastingMode::ECastingMode mode_, const ISpellCaster * caster_, int schoolLvl_, BattleHex destination_) + : ti(s,schoolLvl_, mode_), mode(mode_), destination(destination_), caster(caster_), schoolLvl(schoolLvl_) + {}; + +}; + class DLL_LINKAGE ISpellMechanics { -public: - struct DLL_LINKAGE SpellTargetingContext - { - const CBattleInfoCallback * cb; - CSpell::TargetInfo ti; - ECastingMode::ECastingMode mode; - BattleHex destination; - const ISpellCaster * caster; - int schoolLvl; - - SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode mode_, const ISpellCaster * caster_, int schoolLvl_, BattleHex destination_) - : cb(c), ti(s,schoolLvl_, mode_), mode(mode_), destination(destination_), caster(caster_), schoolLvl(schoolLvl_) - {}; - - }; public: ISpellMechanics(CSpell * s); virtual ~ISpellMechanics(){}; virtual std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0; - virtual std::set getAffectedStacks(SpellTargetingContext & ctx) const = 0; + virtual std::set getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const = 0; virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const = 0; - virtual ESpellCastProblem::ESpellCastProblem canBeCast(const SpellTargetingContext & ctx) const = 0; + virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const = 0; virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const = 0; From a23144b3616d070a1a126008a951f3920df595b4 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 4 Sep 2016 08:19:28 +0300 Subject: [PATCH 20/60] Extracted adventure spell mechanics to distinct class hierarchy. --- lib/spells/AdventureSpellMechanics.cpp | 86 +++++++++++++++++++++++++- lib/spells/AdventureSpellMechanics.h | 41 ++++++++---- lib/spells/CDefaultSpellMechanics.cpp | 83 ------------------------- lib/spells/CDefaultSpellMechanics.h | 12 +--- lib/spells/CSpellHandler.cpp | 19 +++--- lib/spells/CSpellHandler.h | 4 +- lib/spells/ISpellMechanics.cpp | 79 +++++++++++++---------- lib/spells/ISpellMechanics.h | 28 ++++++--- 8 files changed, 193 insertions(+), 159 deletions(-) diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 3241fd25a..5fa1be1ad 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -15,12 +15,94 @@ #include "../CRandomGenerator.h" #include "../mapObjects/CGHeroInstance.h" #include "../NetPacks.h" -#include "../BattleState.h" -#include "../CGameState.h" #include "../CGameInfoCallback.h" #include "../mapping/CMap.h" #include "../CPlayerState.h" +///AdventureSpellMechanics +bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const +{ + if(!owner->isAdventureSpell()) + { + env->complain("Attempt to cast non adventure spell in adventure mode"); + return false; + } + + 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)) + { + env->complain("Hero cannot cast this spell!"); + return false; + } + + if(caster->mana < cost) + { + env->complain("Hero doesn't have enough spell points to cast this spell!"); + return false; + } + + { + AdvmapSpellCast asc; + asc.caster = caster; + asc.spellID = owner->id; + env->sendAndApply(&asc); + } + + switch(applyAdventureEffects(env, parameters)) + { + case ESpellCastResult::OK: + { + SetMana sm; + sm.hid = caster->id; + sm.absolute = false; + sm.val = -cost; + env->sendAndApply(&sm); + return true; + } + break; + case ESpellCastResult::CANCEL: + return true; + } + return false; +} + +ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const +{ + if(owner->hasEffects()) + { + const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + + std::vector bonuses; + + owner->getEffects(bonuses, schoolLevel); + + for(Bonus b : bonuses) + { + GiveBonus gb; + gb.id = parameters.caster->id.getNum(); + gb.bonus = b; + env->sendAndApply(&gb); + } + + return ESpellCastResult::OK; + } + else + { + //There is no generic algorithm of adventure cast + env->complain("Unimplemented adventure spell"); + return ESpellCastResult::ERROR; + } +} + ///SummonBoatMechanics ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const { diff --git a/lib/spells/AdventureSpellMechanics.h b/lib/spells/AdventureSpellMechanics.h index c198290a8..b2ee4bda4 100644 --- a/lib/spells/AdventureSpellMechanics.h +++ b/lib/spells/AdventureSpellMechanics.h @@ -10,47 +10,62 @@ #pragma once -#include "CDefaultSpellMechanics.h" +#include "ISpellMechanics.h" -class ISpellMechanics; -class DefaultSpellMechanics; +enum class ESpellCastResult +{ + OK, + CANCEL,//cast failed but it is not an error + ERROR//internal error occurred +}; -class DLL_LINKAGE SummonBoatMechanics : public DefaultSpellMechanics +class DLL_LINKAGE AdventureSpellMechanics: public IAdventureSpellMechanics { public: - SummonBoatMechanics(CSpell * s): DefaultSpellMechanics(s){}; + AdventureSpellMechanics(CSpell * s): IAdventureSpellMechanics(s){}; + + bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final; +protected: + ///actual adventure cast implementation + virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; +}; + +class DLL_LINKAGE SummonBoatMechanics : public AdventureSpellMechanics +{ +public: + SummonBoatMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE ScuttleBoatMechanics : public DefaultSpellMechanics +class DLL_LINKAGE ScuttleBoatMechanics : public AdventureSpellMechanics { public: - ScuttleBoatMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ScuttleBoatMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE DimensionDoorMechanics : public DefaultSpellMechanics +class DLL_LINKAGE DimensionDoorMechanics : public AdventureSpellMechanics { public: - DimensionDoorMechanics(CSpell * s): DefaultSpellMechanics(s){}; + DimensionDoorMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE TownPortalMechanics : public DefaultSpellMechanics +class DLL_LINKAGE TownPortalMechanics : public AdventureSpellMechanics { public: - TownPortalMechanics(CSpell * s): DefaultSpellMechanics(s){}; + TownPortalMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE ViewMechanics : public DefaultSpellMechanics +class DLL_LINKAGE ViewMechanics : public AdventureSpellMechanics { public: - ViewMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ViewMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; virtual bool filterObject(const CGObjectInstance * obj, const int spellLevel) const = 0; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index c043e88e3..90d97fbdf 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -145,89 +145,6 @@ void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCa } } -bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const -{ - if(!owner->isAdventureSpell()) - { - env->complain("Attempt to cast non adventure spell in adventure mode"); - return false; - } - - 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)) - { - env->complain("Hero cannot cast this spell!"); - return false; - } - - if(caster->mana < cost) - { - env->complain("Hero doesn't have enough spell points to cast this spell!"); - return false; - } - - { - AdvmapSpellCast asc; - asc.caster = caster; - asc.spellID = owner->id; - env->sendAndApply(&asc); - } - - switch(applyAdventureEffects(env, parameters)) - { - case ESpellCastResult::OK: - { - SetMana sm; - sm.hid = caster->id; - sm.absolute = false; - sm.val = -cost; - env->sendAndApply(&sm); - return true; - } - break; - case ESpellCastResult::CANCEL: - return true; - } - return false; -} - -ESpellCastResult DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const -{ - if(owner->hasEffects()) - { - const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - - std::vector bonuses; - - owner->getEffects(bonuses, schoolLevel); - - for(Bonus b : bonuses) - { - GiveBonus gb; - gb.id = parameters.caster->id.getNum(); - gb.bonus = b; - env->sendAndApply(&gb); - } - - return ESpellCastResult::OK; - } - else - { - //There is no generic algorithm of adventure cast - env->complain("Unimplemented adventure spell"); - return ESpellCastResult::ERROR; - } -} - void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const { logGlobal->debugStream() << "Started spell cast. Spell: "<name<<"; mode:"< & logLines, const BattleSpellCast * packet, @@ -54,9 +47,6 @@ public: protected: virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const; - ///actual adventure cast implementation - virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; - void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const; private: void castMagicMirror(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 9a758a978..8f0bb024d 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -91,14 +91,15 @@ CSpell::CSpell(): defaultProbability(0), isRising(false), isDamage(false), isOffensive(false), targetType(ETargetType::NO_TARGET), - mechanics(nullptr) + mechanics(), + adventureMechanics() { levels.resize(GameConstants::SPELL_SCHOOL_LEVELS); } CSpell::~CSpell() { - delete mechanics; + } void CSpell::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const @@ -110,7 +111,12 @@ bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastP { assert(env); - return mechanics->adventureCast(env, parameters); + if(!adventureMechanics.get()) + { + env->complain("Invalid adventure spell cast attempt!"); + return false; + } + return adventureMechanics->adventureCast(env, parameters); } void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const @@ -622,13 +628,8 @@ void CSpell::setup() void CSpell::setupMechanics() { - if(nullptr != mechanics) - { - logGlobal->errorStream() << "Spell " << this->name << ": mechanics already set"; - delete mechanics; - } - mechanics = ISpellMechanics::createMechanics(this); + adventureMechanics = IAdventureSpellMechanics::createMechanics(this); } ///CSpell::AnimationInfo diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 183113baf..f9684713c 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -20,6 +20,7 @@ class CGObjectInstance; class CSpell; class ISpellMechanics; +class IAdventureSpellMechanics; class CLegacyConfigParser; class CGHeroInstance; class CStack; @@ -338,7 +339,8 @@ private: std::vector levels; - ISpellMechanics * mechanics;//(!) do not serialize + std::unique_ptr mechanics;//(!) do not serialize + std::unique_ptr adventureMechanics;//(!) do not serialize }; bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index f7055cd3e..ef0b7a0f1 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -83,72 +83,87 @@ ISpellMechanics::ISpellMechanics(CSpell * s): } -ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s) +std::unique_ptr ISpellMechanics::createMechanics(CSpell * s) { switch (s->id) { case SpellID::ANTI_MAGIC: - return new AntimagicMechanics(s); + return make_unique(s); case SpellID::ACID_BREATH_DAMAGE: - return new AcidBreathDamageMechanics(s); + return make_unique(s); case SpellID::CHAIN_LIGHTNING: - return new ChainLightningMechanics(s); + return make_unique(s); case SpellID::CLONE: - return new CloneMechanics(s); + return make_unique(s); case SpellID::CURE: - return new CureMechanics(s); + return make_unique(s); case SpellID::DEATH_STARE: - return new DeathStareMechanics(s); + return make_unique(s); case SpellID::DISPEL: - return new DispellMechanics(s); + return make_unique(s); case SpellID::DISPEL_HELPFUL_SPELLS: - return new DispellHelpfulMechanics(s); + return make_unique(s); case SpellID::EARTHQUAKE: - return new EarthquakeMechanics(s); + return make_unique(s); case SpellID::FIRE_WALL: case SpellID::FORCE_FIELD: - return new WallMechanics(s); + return make_unique(s); case SpellID::HYPNOTIZE: - return new HypnotizeMechanics(s); + return make_unique(s); case SpellID::LAND_MINE: case SpellID::QUICKSAND: - return new ObstacleMechanics(s); + return make_unique(s); case SpellID::REMOVE_OBSTACLE: - return new RemoveObstacleMechanics(s); + return make_unique(s); case SpellID::SACRIFICE: - return new SacrificeMechanics(s); + return make_unique(s); case SpellID::SUMMON_FIRE_ELEMENTAL: - return new SummonMechanics(s, CreatureID::FIRE_ELEMENTAL); + return make_unique(s, CreatureID::FIRE_ELEMENTAL); case SpellID::SUMMON_EARTH_ELEMENTAL: - return new SummonMechanics(s, CreatureID::EARTH_ELEMENTAL); + return make_unique(s, CreatureID::EARTH_ELEMENTAL); case SpellID::SUMMON_WATER_ELEMENTAL: - return new SummonMechanics(s, CreatureID::WATER_ELEMENTAL); + return make_unique(s, CreatureID::WATER_ELEMENTAL); case SpellID::SUMMON_AIR_ELEMENTAL: - return new SummonMechanics(s, CreatureID::AIR_ELEMENTAL); + return make_unique(s, CreatureID::AIR_ELEMENTAL); case SpellID::TELEPORT: - return new TeleportMechanics(s); + return make_unique(s); + default: + if(s->isRisingSpell()) + return make_unique(s); + else + return make_unique(s); + } +} + +//IAdventureSpellMechanics +IAdventureSpellMechanics::IAdventureSpellMechanics(CSpell * s): + owner(s) +{ + +} + +std::unique_ptr IAdventureSpellMechanics::createMechanics(CSpell * s) +{ + switch (s->id) + { case SpellID::SUMMON_BOAT: - return new SummonBoatMechanics(s); + return make_unique(s); case SpellID::SCUTTLE_BOAT: - return new ScuttleBoatMechanics(s); + return make_unique(s); case SpellID::DIMENSION_DOOR: - return new DimensionDoorMechanics(s); + return make_unique(s); case SpellID::FLY: case SpellID::WATER_WALK: case SpellID::VISIONS: case SpellID::DISGUISE: - return new DefaultSpellMechanics(s); //implemented using bonus system + return make_unique(s); //implemented using bonus system case SpellID::TOWN_PORTAL: - return new TownPortalMechanics(s); + return make_unique(s); case SpellID::VIEW_EARTH: - return new ViewEarthMechanics(s); + return make_unique(s); case SpellID::VIEW_AIR: - return new ViewAirMechanics(s); + return make_unique(s); default: - if(s->isRisingSpell()) - return new SpecialRisingSpellMechanics(s); - else - return new DefaultSpellMechanics(s); + return std::unique_ptr(); } } - diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 1fb343546..8bf0c91df 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -76,12 +76,6 @@ private: void prepare(const CSpell * spell); }; -struct DLL_LINKAGE AdventureSpellCastParameters -{ - const CGHeroInstance * caster; - int3 pos; -}; - struct DLL_LINKAGE SpellTargetingContext { CSpell::TargetInfo ti; @@ -112,13 +106,31 @@ public: virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const = 0; virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0; - virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0; virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0; virtual void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0; - static ISpellMechanics * createMechanics(CSpell * s); + static std::unique_ptr createMechanics(CSpell * s); +protected: + CSpell * owner; +}; + +struct DLL_LINKAGE AdventureSpellCastParameters +{ + const CGHeroInstance * caster; + int3 pos; +}; + +class DLL_LINKAGE IAdventureSpellMechanics +{ +public: + IAdventureSpellMechanics(CSpell * s); + virtual ~IAdventureSpellMechanics() = default; + + virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0; + + static std::unique_ptr createMechanics(CSpell * s); protected: CSpell * owner; }; From f3b7fe947c7363d458a1a3bd0ce34dd73a625426 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Mon, 5 Sep 2016 11:36:25 +0300 Subject: [PATCH 21/60] Reworked spell target existence check. * related to 2269 --- lib/CBattleCallback.cpp | 67 +-------------------------- lib/spells/BattleSpellMechanics.cpp | 37 +++++++++++++++ lib/spells/BattleSpellMechanics.h | 5 ++ lib/spells/CDefaultSpellMechanics.cpp | 7 +++ lib/spells/CDefaultSpellMechanics.h | 2 + lib/spells/CSpellHandler.cpp | 56 +++++++++++++++++++++- lib/spells/CSpellHandler.h | 4 +- lib/spells/ISpellMechanics.h | 3 ++ 8 files changed, 111 insertions(+), 70 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 9fc98713e..d7ecdb799 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1661,79 +1661,14 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell if(!spell->combatSpell) return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL; - const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, caster); + const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, mode, caster); if(specificProblem != ESpellCastProblem::OK) return specificProblem; - if(spell->isNegative() || spell->hasEffects()) - { - bool allStacksImmune = true; - //we are interested only in enemy stacks when casting offensive spells - //TODO: should hero be able to cast non-smart negative spell if all enemy stacks are immune? - auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks(); - for(auto stack : stacks) - { - if(ESpellCastProblem::OK == spell->isImmuneByStack(caster, stack)) - { - allStacksImmune = false; - break; - } - } - - if(allStacksImmune) - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } - if(battleMaxSpellLevel(side) < spell->level) //effect like Recanter's Cloak or Orb of Inhibition return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED; - //checking if there exists an appropriate target - switch(spell->getTargetType()) - { - case CSpell::CREATURE: - if(mode == ECastingMode::HERO_CASTING) - { - const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell)); - bool targetExists = false; - - for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway - { - bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack); - bool casterStack = stack->owner == caster->getOwner(); - - if(!immune) - { - switch (spell->positiveness) - { - case CSpell::POSITIVE: - if(casterStack || !ti.smart) - { - targetExists = true; - break; - } - break; - case CSpell::NEUTRAL: - targetExists = true; - break; - case CSpell::NEGATIVE: - if(!casterStack || !ti.smart) - { - targetExists = true; - break; - } - break; - } - } - } - if(!targetExists) - { - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } - } - break; - } - return ESpellCastProblem::OK; } diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index bea2480f3..705149c67 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -361,6 +361,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl return ESpellCastProblem::OK; } +bool EarthquakeMechanics::requiresCreatureTarget() const +{ + return false; +} + ///HypnotizeMechanics ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const { @@ -504,6 +509,23 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con } } +bool ObstacleMechanics::requiresCreatureTarget() const +{ + switch(owner->id) + { + case SpellID::QUICKSAND: + return false; + case SpellID::LAND_MINE: + return true; + case SpellID::FORCE_FIELD: + return false; + case SpellID::FIRE_WALL: + return true; + default: + return false; + } +} + ///WallMechanics std::vector WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes) const { @@ -606,6 +628,11 @@ bool RemoveObstacleMechanics::canRemove(const CObstacleInstance * obstacle, cons return false; } +bool RemoveObstacleMechanics::requiresCreatureTarget() const +{ + return false; +} + ///RisingSpellMechanics HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const { @@ -700,6 +727,11 @@ int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const return res; } +bool SacrificeMechanics::requiresCreatureTarget() const +{ + return false;//canBeCast do all target existence checks +} + ///SpecialRisingSpellMechanics ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { @@ -774,6 +806,11 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, const env->complain("Summoning didn't summon any!"); } +bool SummonMechanics::requiresCreatureTarget() const +{ + return false; +} + ///TeleportMechanics void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 9339dc383..6f93b31ea 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -81,6 +81,7 @@ class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics public: EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; @@ -97,6 +98,7 @@ class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics public: ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; + bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; @@ -114,6 +116,7 @@ public: RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; + bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; private: @@ -135,6 +138,7 @@ public: SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -155,6 +159,7 @@ public: SummonMechanics(CSpell * s, CreatureID cre): DefaultSpellMechanics(s), creatureToSummon(cre){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; private: diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 90d97fbdf..7aca151b3 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -784,3 +784,10 @@ void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& p sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1); sc.manaGained = 0; } + +bool DefaultSpellMechanics::requiresCreatureTarget() const +{ + //most spells affects creatures somehow regardless of Target Type + //for few exceptions see overrides + return true; +} diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index f1e1b856e..2f36cdfe1 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -44,6 +44,8 @@ public: void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override; + + bool requiresCreatureTarget() const override; protected: virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 8f0bb024d..e03369dbd 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -149,9 +149,61 @@ ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affecte return adjustRawDamage(caster, affectedCreature, calculateRawEffectValue(spellSchoolLevel, usedSpellPower)); } -ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const +ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { - return mechanics->canBeCast(cb, caster); + const ESpellCastProblem::ESpellCastProblem generalProblem = mechanics->canBeCast(cb, caster); + + if(generalProblem != ESpellCastProblem::OK) + return generalProblem; + + //check for creature target existence + if(mechanics->requiresCreatureTarget()) + { + switch(mode) + { + case ECastingMode::HERO_CASTING: + case ECastingMode::CREATURE_ACTIVE_CASTING: + case ECastingMode::ENCHANTER_CASTING: + case ECastingMode::PASSIVE_CASTING: + { + TargetInfo tinfo(this, caster->getSpellSchoolLevel(this), mode); + + bool targetExists = false; + + for(const CStack * stack : cb->battleGetAllStacks()) + { + bool immune = !(stack->isValidTarget(!tinfo.onlyAlive) && ESpellCastProblem::OK == isImmuneByStack(caster, stack)); + bool casterStack = stack->owner == caster->getOwner(); + + if(!immune) + { + switch (positiveness) + { + case CSpell::POSITIVE: + if(casterStack || !tinfo.smart) + targetExists = true; + break; + case CSpell::NEUTRAL: + targetExists = true; + break; + case CSpell::NEGATIVE: + if(!casterStack || !tinfo.smart) + targetExists = true; + break; + } + } + if(targetExists) + break; + } + if(!targetExists) + { + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + } + break; + } + } + return ESpellCastProblem::OK; } std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index f9684713c..93a7b891d 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -266,8 +266,8 @@ public: public: ///internal interface (for callbacks) - ///Checks general but spell-specific problems for all casting modes. Use only during battle. - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const; + ///Checks general but spell-specific problems. Use only during battle. + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster) const; ///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc. ESpellCastProblem::ESpellCastProblem canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 8bf0c91df..96125298c 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -111,6 +111,9 @@ public: virtual void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0; + //if true use generic algorithm for target existence check, see CSpell::canBeCast + virtual bool requiresCreatureTarget() const = 0; + static std::unique_ptr createMechanics(CSpell * s); protected: CSpell * owner; From 82ac0353403d046bd748bc5f545bf0503dc55f04 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Mon, 5 Sep 2016 13:34:48 +0300 Subject: [PATCH 22/60] OO design for obstacle spells. --- lib/CObstacleInstance.cpp | 1 - lib/spells/BattleSpellMechanics.cpp | 212 +++++++++++++------------- lib/spells/BattleSpellMechanics.h | 49 +++++- lib/spells/CDefaultSpellMechanics.cpp | 1 - lib/spells/ISpellMechanics.cpp | 6 +- 5 files changed, 159 insertions(+), 110 deletions(-) diff --git a/lib/CObstacleInstance.cpp b/lib/CObstacleInstance.cpp index 168490e12..0c9d27208 100644 --- a/lib/CObstacleInstance.cpp +++ b/lib/CObstacleInstance.cpp @@ -131,7 +131,6 @@ std::vector SpellCreatedObstacle::getAffectedTiles() const return std::vector(1, pos); case FORCE_FIELD: return SpellID(SpellID::FORCE_FIELD).toSpell()->rangeInHexes(pos, spellLevel, casterSide); - //TODO Fire Wall default: assert(0); return std::vector(); diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 705149c67..db7f37540 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -410,120 +410,69 @@ ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleI return ESpellCastProblem::OK; } -void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const +void ObstacleMechanics::placeObstacle(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const BattleHex & pos) const { - auto placeObstacle = [&, this](const BattleHex & pos) - { - static int obstacleIdToGive = parameters.cb->obstacles.size() - ? (parameters.cb->obstacles.back()->uniqueID+1) - : 0; + const int obstacleIdToGive = parameters.cb->obstacles.size()+1; - auto obstacle = std::make_shared(); - switch(owner->id) // :/ - { - case SpellID::QUICKSAND: - obstacle->obstacleType = CObstacleInstance::QUICKSAND; - obstacle->turnsRemaining = -1; - obstacle->visibleForAnotherSide = false; - break; - case SpellID::LAND_MINE: - obstacle->obstacleType = CObstacleInstance::LAND_MINE; - obstacle->turnsRemaining = -1; - obstacle->visibleForAnotherSide = false; - break; - case SpellID::FIRE_WALL: - obstacle->obstacleType = CObstacleInstance::FIRE_WALL; - obstacle->turnsRemaining = 2; - obstacle->visibleForAnotherSide = true; - break; - case SpellID::FORCE_FIELD: - obstacle->obstacleType = CObstacleInstance::FORCE_FIELD; - obstacle->turnsRemaining = 2; - obstacle->visibleForAnotherSide = true; - break; - default: - //this function cannot be used with spells that do not create obstacles - assert(0); - } + auto obstacle = std::make_shared(); + setupObstacle(obstacle.get()); - obstacle->pos = pos; - obstacle->casterSide = parameters.casterSide; - obstacle->ID = owner->id; - obstacle->spellLevel = parameters.effectLevel; - obstacle->casterSpellPower = parameters.effectPower; - obstacle->uniqueID = obstacleIdToGive++; + obstacle->pos = pos; + obstacle->casterSide = parameters.casterSide; + obstacle->ID = owner->id; + obstacle->spellLevel = parameters.effectLevel; + obstacle->casterSpellPower = parameters.effectPower; + obstacle->uniqueID = obstacleIdToGive; - BattleObstaclePlaced bop; - bop.obstacle = obstacle; - env->sendAndApply(&bop); - }; - - const BattleHex destination = parameters.getFirstDestinationHex(); - - switch(owner->id) - { - case SpellID::QUICKSAND: - case SpellID::LAND_MINE: - { - std::vector availableTiles; - for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1) - { - BattleHex hex = i; - if(hex.getX() > 0 && hex.getX() < 16 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) - availableTiles.push_back(hex); - } - boost::range::random_shuffle(availableTiles); - - const int patchesForSkill[] = {4, 4, 6, 8}; - const int patchesToPut = std::min(patchesForSkill[parameters.spellLvl], availableTiles.size()); - - //land mines or quicksand patches are handled as spell created obstacles - for (int i = 0; i < patchesToPut; i++) - placeObstacle(availableTiles.at(i)); - } - - break; - case SpellID::FORCE_FIELD: - if(!destination.isValid()) - { - env->complain("Invalid destination for FORCE_FIELD"); - return; - } - placeObstacle(destination); - break; - case SpellID::FIRE_WALL: - { - if(!destination.isValid()) - { - env->complain("Invalid destination for FIRE_WALL"); - return; - } - //fire wall is build from multiple obstacles - one fire piece for each affected hex - auto affectedHexes = owner->rangeInHexes(destination, parameters.spellLvl, parameters.casterSide); - for(BattleHex hex : affectedHexes) - placeObstacle(hex); - } - break; - default: - assert(0); - } + BattleObstaclePlaced bop; + bop.obstacle = obstacle; + env->sendAndApply(&bop); } -bool ObstacleMechanics::requiresCreatureTarget() const +///PatchObstacleMechanics +void PatchObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { - switch(owner->id) + std::vector availableTiles; + for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1) { - case SpellID::QUICKSAND: - return false; - case SpellID::LAND_MINE: - return true; - case SpellID::FORCE_FIELD: - return false; - case SpellID::FIRE_WALL: - return true; - default: - return false; + BattleHex hex = i; + if(hex.getX() > 0 && hex.getX() < 16 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) + availableTiles.push_back(hex); } + boost::range::random_shuffle(availableTiles); + + const int patchesForSkill[] = {4, 4, 6, 8}; + const int patchesToPut = std::min(patchesForSkill[parameters.spellLvl], availableTiles.size()); + + //land mines or quicksand patches are handled as spell created obstacles + for (int i = 0; i < patchesToPut; i++) + placeObstacle(env, parameters, availableTiles.at(i)); +} + +///LandMineMechanics +bool LandMineMechanics::requiresCreatureTarget() const +{ + return true; +} + +void LandMineMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::LAND_MINE; + obstacle->turnsRemaining = -1; + obstacle->visibleForAnotherSide = false; +} + +///QuicksandMechanics +bool QuicksandMechanics::requiresCreatureTarget() const +{ + return false; +} + +void QuicksandMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::QUICKSAND; + obstacle->turnsRemaining = -1; + obstacle->visibleForAnotherSide = false; } ///WallMechanics @@ -563,6 +512,59 @@ std::vector WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch return ret; } +///FireWallMechanics +bool FireWallMechanics::requiresCreatureTarget() const +{ + return true; +} + +void FireWallMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const +{ + const BattleHex destination = parameters.getFirstDestinationHex(); + + if(!destination.isValid()) + { + env->complain("Invalid destination for FIRE_WALL"); + return; + } + //firewall is build from multiple obstacles - one fire piece for each affected hex + auto affectedHexes = owner->rangeInHexes(destination, parameters.spellLvl, parameters.casterSide); + for(BattleHex hex : affectedHexes) + placeObstacle(env, parameters, hex); +} + +void FireWallMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::FIRE_WALL; + obstacle->turnsRemaining = 2; + obstacle->visibleForAnotherSide = true; +} + +///ForceFieldMechanics +bool ForceFieldMechanics::requiresCreatureTarget() const +{ + return false; +} + +void ForceFieldMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const +{ + const BattleHex destination = parameters.getFirstDestinationHex(); + + if(!destination.isValid()) + { + env->complain("Invalid destination for FORCE_FIELD"); + return; + } + placeObstacle(env, parameters, destination); +} + +void ForceFieldMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::FORCE_FIELD; + obstacle->turnsRemaining = 2; + obstacle->visibleForAnotherSide = true; +} + ///RemoveObstacleMechanics void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 6f93b31ea..85d3fc41d 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -13,6 +13,7 @@ #include "CDefaultSpellMechanics.h" class CObstacleInstance; +class SpellCreatedObstacle; class DLL_LINKAGE HealingSpellMechanics : public DefaultSpellMechanics { @@ -98,11 +99,37 @@ class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics public: ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; - bool requiresCreatureTarget() const override; +protected: + void placeObstacle(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const BattleHex & pos) const; + virtual void setupObstacle(SpellCreatedObstacle * obstacle) const = 0; +}; + +class PatchObstacleMechanics : public ObstacleMechanics +{ +public: + PatchObstacleMechanics(CSpell * s): ObstacleMechanics(s){}; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; +class DLL_LINKAGE LandMineMechanics : public PatchObstacleMechanics +{ +public: + LandMineMechanics(CSpell * s): PatchObstacleMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + +class DLL_LINKAGE QuicksandMechanics : public PatchObstacleMechanics +{ +public: + QuicksandMechanics(CSpell * s): PatchObstacleMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + class DLL_LINKAGE WallMechanics : public ObstacleMechanics { public: @@ -110,6 +137,26 @@ public: std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override; }; +class DLL_LINKAGE FireWallMechanics : public WallMechanics +{ +public: + FireWallMechanics(CSpell * s): WallMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + +class DLL_LINKAGE ForceFieldMechanics : public WallMechanics +{ +public: + ForceFieldMechanics(CSpell * s): WallMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics { public: diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 7aca151b3..185e4935d 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -258,7 +258,6 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS env->sendAndApply(&si); //reduce number of casts remaining - //TODO: this should be part of BattleSpellCast apply if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING) { assert(parameters.casterStack); diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index ef0b7a0f1..8f3dbac5c 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -106,13 +106,15 @@ std::unique_ptr ISpellMechanics::createMechanics(CSpell * s) case SpellID::EARTHQUAKE: return make_unique(s); case SpellID::FIRE_WALL: + return make_unique(s); case SpellID::FORCE_FIELD: - return make_unique(s); + return make_unique(s); case SpellID::HYPNOTIZE: return make_unique(s); case SpellID::LAND_MINE: + return make_unique(s); case SpellID::QUICKSAND: - return make_unique(s); + return make_unique(s); case SpellID::REMOVE_OBSTACLE: return make_unique(s); case SpellID::SACRIFICE: From 289cbbf2e738f5160d354a34cbbd406370246157 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Mon, 5 Sep 2016 14:17:46 +0300 Subject: [PATCH 23/60] Teach AI how to use massive timed effects. --- AI/BattleAI/BattleAI.cpp | 50 +++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 0bd58c54c..64a024121 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -482,31 +482,43 @@ void CBattleAI::attemptCastingSpell() } case TIMED_EFFECT: { - StackWithBonuses swb; - swb.stack = cb->battleGetStackByPos(ps.dest); - if(!swb.stack) + auto stacksAffected = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, hero, skillLevel, ps.dest); + + if(stacksAffected.empty()) return -1; - Bonus pseudoBonus; - pseudoBonus.sid = ps.spell->id; - pseudoBonus.val = skillLevel; - pseudoBonus.turnsRemain = 1; //TODO - CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus); + int totalGain = 0; - HypotheticChangesToBattleState state; - state.bonusesOfStacks[swb.stack] = &swb; + for(const CStack * sta : stacksAffected) + { + StackWithBonuses swb; + swb.stack = sta; - PotentialTargets pt(swb.stack, state); - auto newValue = pt.bestActionValue(); - auto oldValue = valueOfStack[swb.stack]; - auto gain = newValue - oldValue; - if(swb.stack->owner != playerID) //enemy - gain = -gain; + Bonus pseudoBonus; + pseudoBonus.sid = ps.spell->id; + pseudoBonus.val = skillLevel; + pseudoBonus.turnsRemain = 1; //TODO + CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus); - LOGFL("Casting %s on %s would improve the stack by %d points (from %d to %d)", - ps.spell->name % swb.stack->nodeName() % gain % (oldValue) % (newValue)); + HypotheticChangesToBattleState state; + state.bonusesOfStacks[swb.stack] = &swb; - return gain; + PotentialTargets pt(swb.stack, state); + auto newValue = pt.bestActionValue(); + auto oldValue = valueOfStack[swb.stack]; + auto gain = newValue - oldValue; + if(swb.stack->owner != playerID) //enemy + gain = -gain; + + LOGFL("Casting %s on %s would improve the stack by %d points (from %d to %d)", + ps.spell->name % sta->nodeName() % (gain) % (oldValue) % (newValue)); + + totalGain += gain; + } + + LOGFL("Total gain of cast %s at hex %d is %d", ps.spell->name % (ps.dest.hex) % (totalGain)); + + return totalGain; } default: assert(0); From 1d840a33a5ff7d571972802733f1d541070a2b44 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 04:11:30 +0300 Subject: [PATCH 24/60] Started SpellCastContext rework --- lib/spells/BattleSpellMechanics.cpp | 2 - lib/spells/CDefaultSpellMechanics.cpp | 117 +++++++++++--------------- lib/spells/CDefaultSpellMechanics.h | 25 +++--- 3 files changed, 65 insertions(+), 79 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index db7f37540..94f8eabee 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -225,8 +225,6 @@ ESpellCastProblem::ESpellCastProblem DispellMechanics::isImmuneByStack(const ISp void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { - DefaultSpellMechanics::applyBattleEffects(env, parameters, ctx); - if(parameters.spellLvl > 2) { //expert DISPELL also removes spell-created obstacles diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 185e4935d..86e070f91 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -12,7 +12,6 @@ #include "CDefaultSpellMechanics.h" -#include "../NetPacks.h" #include "../BattleState.h" #include "../CGeneralTextHandler.h" @@ -119,6 +118,24 @@ namespace SRSLPraserHelpers } } +SpellCastContext::SpellCastContext(const DefaultSpellMechanics * mechanics_, const BattleSpellCastParameters & parameters): + mechanics(mechanics_), attackedCres(), sc(), si() +{ + prepareBattleCast(parameters); +} + +void SpellCastContext::prepareBattleCast(const BattleSpellCastParameters & parameters) +{ + sc.side = parameters.casterSide; + sc.id = mechanics->owner->id; + sc.skill = parameters.spellLvl; + sc.tile = parameters.getFirstDestinationHex(); + sc.dmgToDisplay = 0; + sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING; + sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1); + sc.manaGained = 0; +} + ///DefaultSpellMechanics void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { @@ -155,8 +172,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS return; } - BattleSpellCast sc; - prepareBattleCast(parameters, sc); + SpellCastContext ctx(this, parameters); //check it there is opponent hero const ui8 otherSide = 1-parameters.casterSide; @@ -180,35 +196,31 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS vstd::amax(manaChannel, stack->valOfBonuses(Bonus::MANA_CHANNELING)); } } - sc.manaGained = (manaChannel * spellCost) / 100; + ctx.sc.manaGained = (manaChannel * spellCost) / 100; } } logGlobal->debugStream() << "spellCost: " << spellCost; - //calculating affected creatures for all spells - //must be vector, as in Chain Lightning order matters - std::vector attackedCres; //CStack vector is somewhat more suitable than ID vector - auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); - std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres)); + std::copy(creatures.begin(), creatures.end(), std::back_inserter(ctx.attackedCres)); - logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks"; + logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; std::vector reflected;//for magic mirror //checking if creatures resist - handleResistance(env, attackedCres, sc); + handleResistance(env, ctx.attackedCres, ctx.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"; if(tryMagicMirror) { - for(auto s : attackedCres) + for(auto s : ctx.attackedCres) { const int mirrorChance = (s)->valOfBonuses(Bonus::MAGIC_MIRROR); if(env->getRandomGenerator().nextInt(99) < mirrorChance) reflected.push_back(s); } - vstd::erase_if(attackedCres, [&reflected](const CStack * s) + vstd::erase_if(ctx.attackedCres, [&reflected](const CStack * s) { return vstd::contains(reflected, s); }); @@ -218,24 +230,21 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS BattleSpellCast::CustomEffect effect; effect.effect = 3; effect.stack = s->ID; - sc.customEffects.push_back(effect); + ctx.sc.customEffects.push_back(effect); } } - for(auto cre : attackedCres) + for(auto cre : ctx.attackedCres) { - sc.affectedCres.insert(cre->ID); + ctx.sc.affectedCres.insert(cre->ID); } - StacksInjured si; - SpellCastContext ctx(attackedCres, sc, si); applyBattleEffects(env, parameters, ctx); + env->sendAndApply(&ctx.sc); - env->sendAndApply(&sc); - - //spend mana if(parameters.mode == ECastingMode::HERO_CASTING) { + //spend mana SetMana sm; sm.absolute = false; @@ -244,22 +253,18 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS env->sendAndApply(&sm); - if(sc.manaGained > 0) + if(ctx.sc.manaGained > 0) { assert(otherHero); sm.hid = otherHero->id; - sm.val = sc.manaGained; + sm.val = ctx.sc.manaGained; env->sendAndApply(&sm); } } - - if(!si.stacks.empty()) //after spellcast info shows - env->sendAndApply(&si); - - //reduce number of casts remaining - if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING) + else if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING) { + //reduce number of casts remaining assert(parameters.casterStack); BattleSetStackProperty ssp; @@ -269,6 +274,11 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS ssp.absolute = false; env->sendAndApply(&ssp); } + + if(!ctx.si.stacks.empty()) //after spellcast info shows + env->sendAndApply(&ctx.si); + + logGlobal->debugStream() << "Finished spell cast. Spell: "<name<<"; mode:"<debugStream() << "Started spell cast. Spell: "<name<<"; mode: MAGIC_MIRROR"; - if(parameters.mode != ECastingMode::MAGIC_MIRROR) - { - env->complain("MagicMirror: invalid mode"); - return; - } - BattleHex destination = parameters.getFirstDestinationHex(); - if(!destination.isValid()) - { - env->complain("MagicMirror: invalid destination"); - return; - } - BattleSpellCast sc; - prepareBattleCast(parameters, sc); + BattleHex destination = parameters.getFirstDestinationHex(); + SpellCastContext ctx(this, parameters); //calculating affected creatures for all spells - //must be vector, as in Chain Lightning order matters - std::vector attackedCres; //CStack vector is somewhat more suitable than ID vector - auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, destination); - std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres)); + std::copy(creatures.begin(), creatures.end(), std::back_inserter(ctx.attackedCres)); - logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks"; + logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; - handleResistance(env, attackedCres, sc); + handleResistance(env, ctx.attackedCres, ctx.sc); - for(auto cre : attackedCres) + for(auto cre : ctx.attackedCres) { - sc.affectedCres.insert(cre->ID); + ctx.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(&ctx.sc); + if(!ctx.si.stacks.empty()) //after spellcast info shows + env->sendAndApply(&ctx.si); + logGlobal->debugStream() << "Finished spell cast. Spell: "<name<<"; mode: MAGIC_MIRROR"; } @@ -772,18 +767,6 @@ void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, s } } -void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& parameters, BattleSpellCast& sc) const -{ - sc.side = parameters.casterSide; - sc.id = owner->id; - sc.skill = parameters.spellLvl; - sc.tile = parameters.getFirstDestinationHex(); - sc.dmgToDisplay = 0; - sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING; - sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1); - sc.manaGained = 0; -} - bool DefaultSpellMechanics::requiresCreatureTarget() const { //most spells affects creatures somehow regardless of Target Type diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 2f36cdfe1..63b03ace9 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -11,20 +11,24 @@ #pragma once #include "ISpellMechanics.h" +#include "../NetPacks.h" -struct StacksInjured; +class DefaultSpellMechanics; -struct SpellCastContext +class SpellCastContext { - SpellCastContext(std::vector & attackedCres, BattleSpellCast & sc, StacksInjured & si): - attackedCres(attackedCres), sc(sc), si(si) - { - }; - std::vector & attackedCres; - BattleSpellCast & sc; - StacksInjured & si; +public: + const DefaultSpellMechanics * mechanics; + std::vector attackedCres;//must be vector, as in Chain Lightning order matters + BattleSpellCast sc; + StacksInjured si; + + SpellCastContext(const DefaultSpellMechanics * mechanics_, const BattleSpellCastParameters & parameters); +private: + void prepareBattleCast(const BattleSpellCastParameters & parameters); }; +///all combat spells class DLL_LINKAGE DefaultSpellMechanics : public ISpellMechanics { public: @@ -53,5 +57,6 @@ protected: private: void castMagicMirror(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; void handleResistance(const SpellCastEnvironment * env, std::vector & attackedCres, BattleSpellCast & sc) const; - void prepareBattleCast(const BattleSpellCastParameters & parameters, BattleSpellCast & sc) const; + + friend class SpellCastContext; }; From 9ee954edcc26623d4e773e423e17a6c965d5f485 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 05:11:32 +0300 Subject: [PATCH 25/60] WIP on SpellCastContext --- lib/spells/CDefaultSpellMechanics.cpp | 38 ++++++++++++++++++--------- lib/spells/CDefaultSpellMechanics.h | 7 ++++- lib/spells/CreatureSpellMechanics.cpp | 9 ++++--- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 86e070f91..45937c012 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -136,6 +136,26 @@ void SpellCastContext::prepareBattleCast(const BattleSpellCastParameters & param sc.manaGained = 0; } +void SpellCastContext::addDamageToDisplay(const si32 value) +{ + sc.dmgToDisplay += value; +} + +void SpellCastContext::setDamageToDisplay(const si32 value) +{ + sc.dmgToDisplay = value; +} + +void SpellCastContext::sendCastPacket(const SpellCastEnvironment * env) +{ + for(auto sta : attackedCres) + { + sc.affectedCres.insert(sta->ID); + } + + env->sendAndApply(&sc); +} + ///DefaultSpellMechanics void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { @@ -234,13 +254,9 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS } } - for(auto cre : ctx.attackedCres) - { - ctx.sc.affectedCres.insert(cre->ID); - } - applyBattleEffects(env, parameters, ctx); - env->sendAndApply(&ctx.sc); + + ctx.sendCastPacket(env); if(parameters.mode == ECastingMode::HERO_CASTING) { @@ -421,7 +437,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, else bsa.damageAmount = owner->calculateDamage(parameters.caster, attackedCre, parameters.effectLevel, parameters.effectPower) >> chainLightningModifier; - ctx.sc.dmgToDisplay += bsa.damageAmount; + ctx.addDamageToDisplay(bsa.damageAmount); bsa.stackAttacked = (attackedCre)->ID; if(parameters.mode == ECastingMode::ENCHANTER_CASTING) //multiple damage spells cast @@ -720,14 +736,10 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat handleResistance(env, ctx.attackedCres, ctx.sc); - for(auto cre : ctx.attackedCres) - { - ctx.sc.affectedCres.insert(cre->ID); - } - applyBattleEffects(env, parameters, ctx); - env->sendAndApply(&ctx.sc); + ctx.sendCastPacket(env); + if(!ctx.si.stacks.empty()) //after spellcast info shows env->sendAndApply(&ctx.si); diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 63b03ace9..d56cb530d 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -20,10 +20,15 @@ class SpellCastContext public: const DefaultSpellMechanics * mechanics; std::vector attackedCres;//must be vector, as in Chain Lightning order matters - BattleSpellCast sc; + BattleSpellCast sc;//todo: make private StacksInjured si; SpellCastContext(const DefaultSpellMechanics * mechanics_, const BattleSpellCastParameters & parameters); + + void addDamageToDisplay(const si32 value); + void setDamageToDisplay(const si32 value); + + void sendCastPacket(const SpellCastEnvironment * env); private: void prepareBattleCast(const BattleSpellCastParameters & parameters); }; diff --git a/lib/spells/CreatureSpellMechanics.cpp b/lib/spells/CreatureSpellMechanics.cpp index 0879a0c2a..142fabb1f 100644 --- a/lib/spells/CreatureSpellMechanics.cpp +++ b/lib/spells/CreatureSpellMechanics.cpp @@ -20,7 +20,7 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * { //todo: this should be effectValue //calculating dmg to display - ctx.sc.dmgToDisplay = parameters.effectPower; + ctx.setDamageToDisplay(parameters.effectPower); for(auto & attackedCre : ctx.attackedCres) //no immunities { @@ -39,9 +39,12 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { //calculating dmg to display - ctx.sc.dmgToDisplay = parameters.effectPower; + si32 dmgToDisplay = parameters.effectPower; + if(!ctx.attackedCres.empty()) - vstd::amin(ctx.sc.dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack + vstd::amin(dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack + + ctx.setDamageToDisplay(dmgToDisplay); for(auto & attackedCre : ctx.attackedCres) { From 18fc94d709194f200b0e2c1317e9fdebf33a631c Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 06:40:23 +0300 Subject: [PATCH 26/60] WIP on SpellCastContext --- lib/spells/BattleSpellMechanics.cpp | 12 +-- lib/spells/BattleSpellMechanics.h | 2 +- lib/spells/CDefaultSpellMechanics.cpp | 123 ++++++++++++++------------ lib/spells/CDefaultSpellMechanics.h | 9 +- lib/spells/CSpellHandler.cpp | 6 +- lib/spells/CSpellHandler.h | 4 +- lib/spells/ISpellMechanics.h | 4 +- server/CGameHandler.cpp | 2 +- 8 files changed, 85 insertions(+), 77 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 94f8eabee..41051e353 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -62,9 +62,9 @@ void AntimagicMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast } ///ChainLightningMechanics -std::set ChainLightningMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const +std::vector ChainLightningMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const { - std::set attackedCres; + std::vector res; std::set possibleHexes; for(auto stack : cb->battleGetAllStacks()) @@ -85,17 +85,17 @@ std::set ChainLightningMechanics::getAffectedStacks(const CBattl auto stack = cb->battleGetStackByPos(lightningHex, true); if(!stack) break; - attackedCres.insert (stack); + res.push_back(stack); for(auto hex : stack->getHexes()) { - possibleHexes.erase(hex); //can't hit same place twice + possibleHexes.erase(hex); //can't hit same stack twice } if(possibleHexes.empty()) //not enough targets break; - lightningHex = BattleHex::getClosestTile(stack->attackerOwned, ctx.destination, possibleHexes); + lightningHex = BattleHex::getClosestTile(stack->attackerOwned, lightningHex, possibleHexes); } - return attackedCres; + return res; } ///CloneMechanics diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 85d3fc41d..e5a7dcb6b 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -44,7 +44,7 @@ class DLL_LINKAGE ChainLightningMechanics : public DefaultSpellMechanics { public: ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){}; - std::set getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; + std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; }; class DLL_LINKAGE CloneMechanics : public DefaultSpellMechanics diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 45937c012..a7fb9fb72 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -119,9 +119,15 @@ namespace SRSLPraserHelpers } SpellCastContext::SpellCastContext(const DefaultSpellMechanics * mechanics_, const BattleSpellCastParameters & parameters): - mechanics(mechanics_), attackedCres(), sc(), si() + mechanics(mechanics_), attackedCres(), sc(), si(), mode(parameters.mode) { prepareBattleCast(parameters); + logGlobal->debugStream() << "Started spell cast. Spell: " << mechanics->owner->name << "; mode:" << mode; +} + +SpellCastContext::~SpellCastContext() +{ + logGlobal->debugStream() << "Finished spell cast. Spell: " << mechanics->owner->name << "; mode:" << mode; } void SpellCastContext::prepareBattleCast(const BattleSpellCastParameters & parameters) @@ -182,16 +188,48 @@ void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCa } } -void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const +void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const { - logGlobal->debugStream() << "Started spell cast. Spell: "<name<<"; mode:"<complain("No spell-caster provided."); return; } + std::vector reflected;//for magic mirror + + castNormal(env, parameters, reflected); + + //Magic Mirror effect + for(auto & attackedCre : reflected) + { + TStacks mirrorTargets = parameters.cb->battleGetStacksIf([this, parameters](const CStack * battleStack) + { + //Get all enemy stacks. Magic mirror can reflect to immune creature (with no effect) + return battleStack->owner == parameters.casterColor && battleStack->isValidTarget(false); + }); + + if(!mirrorTargets.empty()) + { + int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position; + + BattleSpellCastParameters mirrorParameters(parameters.cb, attackedCre, owner); + mirrorParameters.spellLvl = 0; + mirrorParameters.aimToHex(targetHex); + mirrorParameters.mode = ECastingMode::MAGIC_MIRROR; + mirrorParameters.selectedStack = nullptr; + mirrorParameters.spellLvl = parameters.spellLvl; + mirrorParameters.effectLevel = parameters.effectLevel; + mirrorParameters.effectPower = parameters.effectPower; + mirrorParameters.effectValue = parameters.effectValue; + mirrorParameters.enchantPower = parameters.enchantPower; + castMagicMirror(env, mirrorParameters); + } + } +} + +void DefaultSpellMechanics::castNormal(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const +{ SpellCastContext ctx(this, parameters); //check it there is opponent hero @@ -221,12 +259,11 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS } logGlobal->debugStream() << "spellCost: " << spellCost; - auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); - std::copy(creatures.begin(), creatures.end(), std::back_inserter(ctx.attackedCres)); + ctx.attackedCres = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; - std::vector reflected;//for magic mirror + //checking if creatures resist handleResistance(env, ctx.attackedCres, ctx.sc); //it is actual spell and can be reflected to single target, no recurrence @@ -293,35 +330,25 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS if(!ctx.si.stacks.empty()) //after spellcast info shows env->sendAndApply(&ctx.si); +} +void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const +{ + SpellCastContext ctx(this, parameters); - logGlobal->debugStream() << "Finished spell cast. Spell: "<name<<"; mode:"<battleGetStacksIf([this, parameters](const CStack * battleStack) - { - //Get all enemy stacks. Magic mirror can reflect to immune creature (with no effect) - return battleStack->owner == parameters.casterColor && battleStack->isValidTarget(false); - }); + //calculating affected creatures for all spells + ctx.attackedCres = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); - if(!mirrorTargets.empty()) - { - int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position; + logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; - BattleSpellCastParameters mirrorParameters(parameters.cb, attackedCre, owner); - mirrorParameters.spellLvl = 0; - mirrorParameters.aimToHex(targetHex); - mirrorParameters.mode = ECastingMode::MAGIC_MIRROR; - mirrorParameters.selectedStack = nullptr; - mirrorParameters.spellLvl = parameters.spellLvl; - mirrorParameters.effectLevel = parameters.effectLevel; - mirrorParameters.effectPower = parameters.effectPower; - mirrorParameters.effectValue = parameters.effectValue; - mirrorParameters.enchantPower = parameters.enchantPower; - castMagicMirror(env, mirrorParameters); - } - } + handleResistance(env, ctx.attackedCres, ctx.sc); + + applyBattleEffects(env, parameters, ctx); + + ctx.sendCastPacket(env); + + if(!ctx.si.stacks.empty()) //after spellcast info shows + env->sendAndApply(&ctx.si); } void DefaultSpellMechanics::battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, @@ -610,7 +637,7 @@ std::vector DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, return ret; } -std::set DefaultSpellMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const +std::vector DefaultSpellMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const { std::set attackedCres;//std::set to exclude multiple occurrences of two hex creatures @@ -678,7 +705,10 @@ std::set DefaultSpellMechanics::getAffectedStacks(const CBattleI } } - return attackedCres; + std::vector res; + std::copy(attackedCres.begin(), attackedCres.end(), std::back_inserter(res)); + + return res; } ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const @@ -721,31 +751,6 @@ void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast } } -void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, BattleSpellCastParameters& parameters) const -{ - logGlobal->debugStream() << "Started spell cast. Spell: "<name<<"; mode: MAGIC_MIRROR"; - - BattleHex destination = parameters.getFirstDestinationHex(); - SpellCastContext ctx(this, parameters); - - //calculating affected creatures for all spells - auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, destination); - std::copy(creatures.begin(), creatures.end(), std::back_inserter(ctx.attackedCres)); - - logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; - - handleResistance(env, ctx.attackedCres, ctx.sc); - - applyBattleEffects(env, parameters, ctx); - - ctx.sendCastPacket(env); - - if(!ctx.si.stacks.empty()) //after spellcast info shows - env->sendAndApply(&ctx.si); - - logGlobal->debugStream() << "Finished spell cast. Spell: "<name<<"; mode: MAGIC_MIRROR"; -} - void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, std::vector& attackedCres, BattleSpellCast& sc) const { //checking if creatures resist diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index d56cb530d..ac6e8ba98 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -22,8 +22,10 @@ public: std::vector attackedCres;//must be vector, as in Chain Lightning order matters BattleSpellCast sc;//todo: make private StacksInjured si; + ECastingMode::ECastingMode mode; SpellCastContext(const DefaultSpellMechanics * mechanics_, const BattleSpellCastParameters & parameters); + virtual ~SpellCastContext(); void addDamageToDisplay(const si32 value); void setDamageToDisplay(const si32 value); @@ -40,7 +42,7 @@ public: DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; - std::set getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; + std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; @@ -49,7 +51,7 @@ public: virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override; - void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override final; + void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const override final; void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override; @@ -60,7 +62,8 @@ protected: void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const; private: - void castMagicMirror(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; + void castNormal(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const; + void castMagicMirror(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const; void handleResistance(const SpellCastEnvironment * env, std::vector & attackedCres, BattleSpellCast & sc) const; friend class SpellCastContext; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index e03369dbd..58e4f4ef8 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -119,7 +119,7 @@ bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastP return adventureMechanics->adventureCast(env, parameters); } -void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const +void CSpell::battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const { assert(env); if(parameters.destinations.size()<1) @@ -211,11 +211,11 @@ std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes); } -std::set CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const +std::vector CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const { SpellTargetingContext ctx(this, mode, caster, spellLvl, destination); - std::set attackedCres = mechanics->getAffectedStacks(cb, ctx); + std::vector attackedCres = mechanics->getAffectedStacks(cb, ctx); //now handle immunities auto predicate = [&, this](const CStack * s)->bool diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 93a7b891d..30463b8ef 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -218,7 +218,7 @@ public: ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; ///selects from allStacks actually affected stacks - std::set getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const; + std::vector getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const; si32 getCost(const int skillLevel) const; @@ -279,7 +279,7 @@ public: ///May be executed on client side by (future) non-cheat-proof scripts. bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; - void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; + void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const; public: ///Client-server logic. Has direct write access to GameState. diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 96125298c..4d6a1ca92 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -97,7 +97,7 @@ public: virtual ~ISpellMechanics(){}; virtual std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0; - virtual std::set getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const = 0; + virtual std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const = 0; virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const = 0; @@ -106,7 +106,7 @@ public: virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const = 0; virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0; - virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0; + virtual void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const = 0; virtual void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 8d78c29d6..63c81e080 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -842,7 +842,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt //TODO: should spell override creature`s projectile? - std::set attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att, bonus->val, targetHex); + auto attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att, bonus->val, targetHex); //TODO: get exact attacked hex for defender From f1d0bede11b6ac98170837a27d27e608a2e2b9dc Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 07:05:55 +0300 Subject: [PATCH 27/60] Moved all affected stacks calculations to mechanics --- lib/spells/BattleSpellMechanics.cpp | 2 +- lib/spells/BattleSpellMechanics.h | 3 ++- lib/spells/CDefaultSpellMechanics.cpp | 20 ++++++++++++++++++++ lib/spells/CDefaultSpellMechanics.h | 6 +++++- lib/spells/CSpellHandler.cpp | 15 +-------------- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 41051e353..37c2ddfc5 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -62,7 +62,7 @@ void AntimagicMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast } ///ChainLightningMechanics -std::vector ChainLightningMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const +std::vector ChainLightningMechanics::calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { std::vector res; diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index e5a7dcb6b..f2812d988 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -44,7 +44,8 @@ class DLL_LINKAGE ChainLightningMechanics : public DefaultSpellMechanics { public: ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){}; - std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; +protected: + std::vector calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; }; class DLL_LINKAGE CloneMechanics : public DefaultSpellMechanics diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index a7fb9fb72..37229855d 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -638,6 +638,13 @@ std::vector DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, } std::vector DefaultSpellMechanics::getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const +{ + std::vector attackedCres = calculateAffectedStacks(cb, ctx); + handleImmunities(cb, ctx, attackedCres); + return attackedCres; +} + +std::vector DefaultSpellMechanics::calculateAffectedStacks(const CBattleInfoCallback* cb, const SpellTargetingContext& ctx) const { std::set attackedCres;//std::set to exclude multiple occurrences of two hex creatures @@ -751,6 +758,19 @@ void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast } } +void DefaultSpellMechanics::handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const +{ + //now handle immunities + auto predicate = [&, this](const CStack * s)->bool + { + bool hitDirectly = ctx.ti.alwaysHitDirectly && s->coversPos(ctx.destination); + bool notImmune = (ESpellCastProblem::OK == owner->isImmuneByStack(ctx.caster, s)); + + return !(hitDirectly || notImmune); + }; + vstd::erase_if(stacks, predicate); +} + void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, std::vector& attackedCres, BattleSpellCast& sc) const { //checking if creatures resist diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index ac6e8ba98..1e5b0ee09 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -42,7 +42,7 @@ public: DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; - std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override; + std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override final; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; @@ -60,6 +60,10 @@ public: protected: virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const; + virtual std::vector calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const; + virtual void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const; + +protected: void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const; private: void castNormal(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 58e4f4ef8..0c9767771 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -214,20 +214,7 @@ std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, std::vector CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster, int spellLvl, BattleHex destination) const { SpellTargetingContext ctx(this, mode, caster, spellLvl, destination); - - std::vector attackedCres = mechanics->getAffectedStacks(cb, ctx); - - //now handle immunities - auto predicate = [&, this](const CStack * s)->bool - { - bool hitDirectly = ctx.ti.alwaysHitDirectly && s->coversPos(destination); - bool notImmune = (ESpellCastProblem::OK == isImmuneByStack(caster, s)); - - return !(hitDirectly || notImmune); - }; - vstd::erase_if(attackedCres, predicate); - - return attackedCres; + return mechanics->getAffectedStacks(cb, ctx);; } CSpell::ETargetType CSpell::getTargetType() const From 39c2c6cde76aaca5bb7beb7db1b2c691d8783001 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 07:16:32 +0300 Subject: [PATCH 28/60] Ensure no immunity to AcidBreathDamage --- lib/spells/CreatureSpellMechanics.cpp | 5 +++++ lib/spells/CreatureSpellMechanics.h | 1 + 2 files changed, 6 insertions(+) diff --git a/lib/spells/CreatureSpellMechanics.cpp b/lib/spells/CreatureSpellMechanics.cpp index 142fabb1f..4fcc80248 100644 --- a/lib/spells/CreatureSpellMechanics.cpp +++ b/lib/spells/CreatureSpellMechanics.cpp @@ -35,6 +35,11 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * } } +void AcidBreathDamageMechanics::handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const +{ + //no immunities +} + ///DeathStareMechanics void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { diff --git a/lib/spells/CreatureSpellMechanics.h b/lib/spells/CreatureSpellMechanics.h index 7cd75af03..56125f73f 100644 --- a/lib/spells/CreatureSpellMechanics.h +++ b/lib/spells/CreatureSpellMechanics.h @@ -19,6 +19,7 @@ public: AcidBreathDamageMechanics(CSpell * s): DefaultSpellMechanics(s){}; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; + void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const override; }; class DLL_LINKAGE DeathStareMechanics : public DefaultSpellMechanics From 1e32c71e4706375eb175c0b349537dd710eade03 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 08:20:17 +0300 Subject: [PATCH 29/60] Make sure that there is no unwanted stack affect by spells. --- lib/spells/BattleSpellMechanics.h | 16 ++++++++-------- lib/spells/CDefaultSpellMechanics.cpp | 8 +++++++- lib/spells/CDefaultSpellMechanics.h | 11 ++++++++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index f2812d988..dadfa36b4 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -78,10 +78,10 @@ protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; -class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics +class DLL_LINKAGE EarthquakeMechanics : public SpecialSpellMechanics { public: - EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){}; + EarthquakeMechanics(CSpell * s): SpecialSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: @@ -95,10 +95,10 @@ public: ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; }; -class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics +class DLL_LINKAGE ObstacleMechanics : public SpecialSpellMechanics { public: - ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ObstacleMechanics(CSpell * s): SpecialSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; protected: void placeObstacle(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const BattleHex & pos) const; @@ -158,10 +158,10 @@ protected: void setupObstacle(SpellCreatedObstacle * obstacle) const override; }; -class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics +class DLL_LINKAGE RemoveObstacleMechanics : public SpecialSpellMechanics { public: - RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; + RemoveObstacleMechanics(CSpell * s): SpecialSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; bool requiresCreatureTarget() const override; @@ -201,10 +201,10 @@ public: ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; }; -class DLL_LINKAGE SummonMechanics : public DefaultSpellMechanics +class DLL_LINKAGE SummonMechanics : public SpecialSpellMechanics { public: - SummonMechanics(CSpell * s, CreatureID cre): DefaultSpellMechanics(s), creatureToSummon(cre){}; + SummonMechanics(CSpell * s, CreatureID cre): SpecialSpellMechanics(s), creatureToSummon(cre){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 37229855d..4105e0151 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -578,7 +578,7 @@ std::vector DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, std::vector ret; std::string rng = owner->getLevelInfo(schoolLvl).range + ','; //copy + artificial comma for easier handling - if(rng.size() >= 2 && rng[0] != 'X') //there is at lest one hex in range (+artificial comma) + if(rng.size() >= 2 && rng[0] != 'X') //there is at least one hex in range (+artificial comma) { std::string number1, number2; int beg, end; @@ -810,3 +810,9 @@ bool DefaultSpellMechanics::requiresCreatureTarget() const //for few exceptions see overrides return true; } + + +std::vector SpecialSpellMechanics::calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const +{ + return std::vector(); +} diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 1e5b0ee09..ad9a5f1b1 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -15,7 +15,7 @@ class DefaultSpellMechanics; -class SpellCastContext +class DLL_LINKAGE SpellCastContext { public: const DefaultSpellMechanics * mechanics; @@ -72,3 +72,12 @@ private: friend class SpellCastContext; }; + +///not affecting creatures directly +class DLL_LINKAGE SpecialSpellMechanics : public DefaultSpellMechanics +{ +public: + SpecialSpellMechanics(CSpell * s): DefaultSpellMechanics(s){}; +protected: + std::vector calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; +}; From 0241c6e843c47547b67cedefb2c8c9c9ae54ba91 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 08:56:31 +0300 Subject: [PATCH 30/60] Improved spell range calculation. * should now support smart custom range AOE spells --- lib/spells/CDefaultSpellMechanics.cpp | 73 ++++++++++++--------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 4105e0151..d28cd5a40 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -651,64 +651,55 @@ std::vector DefaultSpellMechanics::calculateAffectedStacks(const const ui8 attackerSide = cb->playerToSide(ctx.caster->getOwner()) == 1; const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide); - //TODO: more generic solution for mass spells - if(owner->getLevelInfo(ctx.schoolLvl).range.size() > 1) //custom many-hex range + auto mainFilter = [=](const CStack * s) { - for(BattleHex hex : attackedHexes) + const bool positiveToAlly = owner->isPositive() && s->owner == ctx.caster->getOwner(); + const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.caster->getOwner(); + const bool validTarget = s->isValidTarget(!ctx.ti.onlyAlive); //todo: this should be handled by spell class + const bool positivenessFlag = !ctx.ti.smart || owner->isNeutral() || positiveToAlly || negativeToEnemy; + + return positivenessFlag && validTarget; + }; + + if(ctx.ti.type == CSpell::CREATURE && attackedHexes.size() == 1) + { + //for single target spells we must select one target. Alive stack is preferred (issue #1763) + + auto predicate = [&](const CStack * s) { - if(const CStack * st = cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) - { - attackedCres.insert(st); - } - } - } - else if(ctx.ti.type == CSpell::CREATURE) - { - auto predicate = [=](const CStack * s){ - const bool positiveToAlly = owner->isPositive() && s->owner == ctx.caster->getOwner(); - const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.caster->getOwner(); - const bool validTarget = s->isValidTarget(!ctx.ti.onlyAlive); //todo: this should be handled by spell class - - //for single target spells select stacks covering destination tile - const bool rangeCovers = ctx.ti.massive || s->coversPos(ctx.destination); - //handle smart targeting - const bool positivenessFlag = !ctx.ti.smart || owner->isNeutral() || positiveToAlly || negativeToEnemy; - - return rangeCovers && positivenessFlag && validTarget; + return s->coversPos(attackedHexes.at(0)) && mainFilter(s); }; TStacks stacks = cb->battleGetStacksIf(predicate); - if(ctx.ti.massive) + for(auto stack : stacks) { - //for massive spells add all targets - for (auto stack : stacks) + if(stack->alive()) + { attackedCres.insert(stack); - } - else - { - //for single target spells we must select one target. Alive stack is preferred (issue #1763) - for(auto stack : stacks) - { - if(stack->alive()) - { - attackedCres.insert(stack); - break; - } + break; } + } - if(attackedCres.empty() && !stacks.empty()) - { - attackedCres.insert(stacks.front()); - } + if(attackedCres.empty() && !stacks.empty()) + { + attackedCres.insert(stacks.front()); } + + } + else if(ctx.ti.massive) + { + TStacks stacks = cb->battleGetStacksIf(mainFilter); + for (auto stack : stacks) + attackedCres.insert(stack); } else //custom range from attackedHexes { for(BattleHex hex : attackedHexes) { if(const CStack * st = cb->battleGetStackByPos(hex, ctx.ti.onlyAlive)) - attackedCres.insert(st); + if(mainFilter(st)) + attackedCres.insert(st);; } } From f3d9e718bf871ccbdd597ce3ccfd9e5642238008 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 10:52:54 +0300 Subject: [PATCH 31/60] Handle AcidBreathDamage immunity in standard way. --- lib/spells/CDefaultSpellMechanics.cpp | 7 ++++--- lib/spells/CDefaultSpellMechanics.h | 2 +- lib/spells/CreatureSpellMechanics.cpp | 20 +++++++++++++++++--- lib/spells/CreatureSpellMechanics.h | 4 +++- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index d28cd5a40..ac745dc60 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -266,7 +266,9 @@ void DefaultSpellMechanics::castNormal(const SpellCastEnvironment * env, const B //checking if creatures resist handleResistance(env, ctx.attackedCres, ctx.sc); - //it is actual spell and can be reflected to single target, no recurrence + + //reflection is applied only to negative spells + //if 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"; if(tryMagicMirror) { @@ -765,7 +767,7 @@ void DefaultSpellMechanics::handleImmunities(const CBattleInfoCallback * cb, con void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, std::vector& attackedCres, BattleSpellCast& sc) const { //checking if creatures resist - //resistance/reflection is applied only to negative spells + //resistance is applied only to negative spells if(owner->isNegative()) { std::vector resisted; @@ -802,7 +804,6 @@ bool DefaultSpellMechanics::requiresCreatureTarget() const return true; } - std::vector SpecialSpellMechanics::calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { return std::vector(); diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index ad9a5f1b1..e856d75ea 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -61,13 +61,13 @@ protected: virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const; virtual std::vector calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const; - virtual void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const; protected: void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const; private: void castNormal(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const; void castMagicMirror(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const; + void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const; void handleResistance(const SpellCastEnvironment * env, std::vector & attackedCres, BattleSpellCast & sc) const; friend class SpellCastContext; diff --git a/lib/spells/CreatureSpellMechanics.cpp b/lib/spells/CreatureSpellMechanics.cpp index 4fcc80248..eba0ba447 100644 --- a/lib/spells/CreatureSpellMechanics.cpp +++ b/lib/spells/CreatureSpellMechanics.cpp @@ -22,7 +22,7 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * //calculating dmg to display ctx.setDamageToDisplay(parameters.effectPower); - for(auto & attackedCre : ctx.attackedCres) //no immunities + for(auto & attackedCre : ctx.attackedCres) { BattleStackAttacked bsa; bsa.flags |= BattleStackAttacked::SPELL_EFFECT; @@ -35,9 +35,23 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * } } -void AcidBreathDamageMechanics::handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const +ESpellCastProblem::ESpellCastProblem AcidBreathDamageMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const { - //no immunities + //just in case + if(!obj->alive()) + return ESpellCastProblem::WRONG_SPELL_TARGET; + + //there should be no immunities by design + //but make it a bit configurable + //ignore all immunities, except specific absolute immunity + { + //SPELL_IMMUNITY absolute case + std::stringstream cachingStr; + cachingStr << "type_" << Bonus::SPELL_IMMUNITY << "subtype_" << owner->id.toEnum() << "addInfo_1"; + if(obj->hasBonus(Selector::typeSubtypeInfo(Bonus::SPELL_IMMUNITY, owner->id.toEnum(), 1), cachingStr.str())) + return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; + } + return ESpellCastProblem::OK; } ///DeathStareMechanics diff --git a/lib/spells/CreatureSpellMechanics.h b/lib/spells/CreatureSpellMechanics.h index 56125f73f..38affa209 100644 --- a/lib/spells/CreatureSpellMechanics.h +++ b/lib/spells/CreatureSpellMechanics.h @@ -17,9 +17,11 @@ class DLL_LINKAGE AcidBreathDamageMechanics : public DefaultSpellMechanics { public: AcidBreathDamageMechanics(CSpell * s): DefaultSpellMechanics(s){}; + + ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; + protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; - void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const override; }; class DLL_LINKAGE DeathStareMechanics : public DefaultSpellMechanics From cc4362211c9ec5cd22f97cc354a1c165c068fe91 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 11:03:36 +0300 Subject: [PATCH 32/60] Extracted MagicMirror handling --- lib/spells/CDefaultSpellMechanics.cpp | 57 ++++++++++++++------------- lib/spells/CDefaultSpellMechanics.h | 2 + 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index ac745dc60..22fc16394 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -263,35 +263,9 @@ void DefaultSpellMechanics::castNormal(const SpellCastEnvironment * env, const B logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; - - //checking if creatures resist handleResistance(env, ctx.attackedCres, ctx.sc); - //reflection is applied only to negative spells - //if 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"; - if(tryMagicMirror) - { - for(auto s : ctx.attackedCres) - { - const int mirrorChance = (s)->valOfBonuses(Bonus::MAGIC_MIRROR); - if(env->getRandomGenerator().nextInt(99) < mirrorChance) - reflected.push_back(s); - } - - vstd::erase_if(ctx.attackedCres, [&reflected](const CStack * s) - { - return vstd::contains(reflected, s); - }); - - for(auto s : reflected) - { - BattleSpellCast::CustomEffect effect; - effect.effect = 3; - effect.stack = s->ID; - ctx.sc.customEffects.push_back(effect); - } - } + handleMagicMirror(env, ctx, reflected); applyBattleEffects(env, parameters, ctx); @@ -764,6 +738,35 @@ void DefaultSpellMechanics::handleImmunities(const CBattleInfoCallback * cb, con vstd::erase_if(stacks, predicate); } +void DefaultSpellMechanics::handleMagicMirror(const SpellCastEnvironment * env, SpellCastContext & ctx, std::vector & reflected) const +{ + //reflection is applied only to negative spells + //if 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"; + if(tryMagicMirror) + { + for(auto s : ctx.attackedCres) + { + const int mirrorChance = (s)->valOfBonuses(Bonus::MAGIC_MIRROR); + if(env->getRandomGenerator().nextInt(99) < mirrorChance) + reflected.push_back(s); + } + + vstd::erase_if(ctx.attackedCres, [&reflected](const CStack * s) + { + return vstd::contains(reflected, s); + }); + + for(auto s : reflected) + { + BattleSpellCast::CustomEffect effect; + effect.effect = 3; + effect.stack = s->ID; + ctx.sc.customEffects.push_back(effect); + } + } +} + void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, std::vector& attackedCres, BattleSpellCast& sc) const { //checking if creatures resist diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index e856d75ea..eec4700a5 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -67,7 +67,9 @@ protected: private: void castNormal(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const; void castMagicMirror(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const; + void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const; + void handleMagicMirror(const SpellCastEnvironment * env, SpellCastContext & ctx, std::vector & reflected) const; void handleResistance(const SpellCastEnvironment * env, std::vector & attackedCres, BattleSpellCast & sc) const; friend class SpellCastContext; From 686cd00c686ad88c3bfddb041ae3f36ee4260a99 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 12:26:01 +0300 Subject: [PATCH 33/60] Cleanup. --- lib/spells/BattleSpellMechanics.cpp | 43 ++++++++++----------------- lib/spells/CDefaultSpellMechanics.cpp | 17 +++++------ lib/spells/CDefaultSpellMechanics.h | 2 +- lib/spells/ISpellMechanics.cpp | 8 +++-- lib/spells/ISpellMechanics.h | 1 - server/CGameHandler.cpp | 8 ++--- 6 files changed, 30 insertions(+), 49 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 37c2ddfc5..486b9900f 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -685,11 +685,7 @@ void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, co { victim = parameters.destinations[1].stackValue; } - else - { - //todo: remove and report error - victim = parameters.selectedStack; - } + if(nullptr == victim) { env->complain("SacrificeMechanics: No stack to sacrifice"); @@ -712,11 +708,7 @@ int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const { victim = parameters.destinations[1].stackValue; } - else - { - //todo: remove and report error - victim = parameters.selectedStack; - } + if(nullptr == victim) { env->complain("SacrificeMechanics: No stack to sacrifice"); @@ -817,20 +809,22 @@ void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, con //todo: check legal teleport if(parameters.destinations.size() == 2) { - //first destination creature to move - const CStack * target = parameters.destinations[0].stackValue; - if(nullptr == target) - { - env->complain("TeleportMechanics: no stack to teleport"); - return; - } - //second destination hex to move to - const BattleHex destination = parameters.destinations[1].hexValue; + //first destination hex to move to + const BattleHex destination = parameters.destinations[0].hexValue; if(!destination.isValid()) { env->complain("TeleportMechanics: invalid teleport destination"); return; } + + //second destination creature to move + const CStack * target = parameters.destinations[1].stackValue; + if(nullptr == target) + { + env->complain("TeleportMechanics: no stack to teleport"); + return; + } + BattleStackMoved bsm; bsm.distance = -1; bsm.stack = target->ID; @@ -842,15 +836,8 @@ void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, con } else { - //todo: remove and report error - BattleStackMoved bsm; - bsm.distance = -1; - bsm.stack = parameters.selectedStack->ID; - std::vector tiles; - tiles.push_back(parameters.getFirstDestinationHex()); - bsm.tilesToMove = tiles; - bsm.teleporting = true; - env->sendAndApply(&bsm); + env->complain("TeleportMechanics: 2 destinations required."); + return; } } diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 22fc16394..e15db46d6 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -217,7 +217,6 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const B mirrorParameters.spellLvl = 0; mirrorParameters.aimToHex(targetHex); mirrorParameters.mode = ECastingMode::MAGIC_MIRROR; - mirrorParameters.selectedStack = nullptr; mirrorParameters.spellLvl = parameters.spellLvl; mirrorParameters.effectLevel = parameters.effectLevel; mirrorParameters.effectPower = parameters.effectPower; @@ -261,9 +260,9 @@ void DefaultSpellMechanics::castNormal(const SpellCastEnvironment * env, const B ctx.attackedCres = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); - logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; + logGlobal->debugStream() << "will affect " << ctx.attackedCres.size() << " stacks"; - handleResistance(env, ctx.attackedCres, ctx.sc); + handleResistance(env, ctx); handleMagicMirror(env, ctx, reflected); @@ -317,7 +316,7 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment * env, co logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; - handleResistance(env, ctx.attackedCres, ctx.sc); + handleResistance(env, ctx); applyBattleEffects(env, parameters, ctx); @@ -543,7 +542,6 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, if(!sse.stacks.empty()) env->sendAndApply(&sse); - } } @@ -661,7 +659,6 @@ std::vector DefaultSpellMechanics::calculateAffectedStacks(const { attackedCres.insert(stacks.front()); } - } else if(ctx.ti.massive) { @@ -767,14 +764,14 @@ void DefaultSpellMechanics::handleMagicMirror(const SpellCastEnvironment * env, } } -void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, std::vector& attackedCres, BattleSpellCast& sc) const +void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, SpellCastContext & ctx) const { //checking if creatures resist //resistance is applied only to negative spells if(owner->isNegative()) { std::vector resisted; - for(auto s : attackedCres) + for(auto s : ctx.attackedCres) { //magic resistance const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in % @@ -785,7 +782,7 @@ void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, s } } - vstd::erase_if(attackedCres, [&resisted](const CStack * s) + vstd::erase_if(ctx.attackedCres, [&resisted](const CStack * s) { return vstd::contains(resisted, s); }); @@ -795,7 +792,7 @@ void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, s BattleSpellCast::CustomEffect effect; effect.effect = 78; effect.stack = s->ID; - sc.customEffects.push_back(effect); + ctx.sc.customEffects.push_back(effect); } } } diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index eec4700a5..2ce398314 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -70,7 +70,7 @@ private: void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const; void handleMagicMirror(const SpellCastEnvironment * env, SpellCastContext & ctx, std::vector & reflected) const; - void handleResistance(const SpellCastEnvironment * env, std::vector & attackedCres, BattleSpellCast & sc) const; + void handleResistance(const SpellCastEnvironment * env, SpellCastContext & ctx) const; friend class SpellCastContext; }; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 8f3dbac5c..7843530fd 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -37,7 +37,7 @@ BattleSpellCastParameters::Destination::Destination(const BattleHex & destinatio BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell) : cb(cb), caster(caster), casterColor(caster->getOwner()), casterSide(cb->whatSide(casterColor)), casterHero(nullptr), - mode(ECastingMode::HERO_CASTING), casterStack(nullptr), selectedStack(nullptr), + mode(ECastingMode::HERO_CASTING), casterStack(nullptr), spellLvl(-1), effectLevel(-1), effectPower(0), enchantPower(0), effectValue(0) { casterStack = dynamic_cast(caster); @@ -52,10 +52,12 @@ void BattleSpellCastParameters::aimToHex(const BattleHex& destination) void BattleSpellCastParameters::aimToStack(const CStack * destination) { - destinations.push_back(Destination(destination)); + if(nullptr == destination) + logGlobal->error("BattleSpellCastParameters::aimToStack invalid stack."); + else + destinations.push_back(Destination(destination)); } - BattleHex BattleSpellCastParameters::getFirstDestinationHex() const { return destinations.at(0).hexValue; diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 4d6a1ca92..4ece618c1 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -60,7 +60,6 @@ public: const CGHeroInstance * casterHero; //deprecated ECastingMode::ECastingMode mode; const CStack * casterStack; //deprecated - const CStack * selectedStack;//deprecated ///spell school level int spellLvl; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 63c81e080..22d8f6a8c 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4171,7 +4171,6 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) parameters.effectLevel = parameters.spellLvl; parameters.mode = ECastingMode::CREATURE_ACTIVE_CASTING; parameters.aimToHex(destination);//todo: allow multiple destinations - parameters.selectedStack = nullptr; spell->battleCast(spellEnv, parameters); } sendAndApply(&end_action); @@ -4370,7 +4369,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) BattleSpellCastParameters parameters(gs->curB, h, s); parameters.aimToHex(ba.destinationTile);//todo: allow multiple destinations parameters.mode = ECastingMode::HERO_CASTING; - parameters.selectedStack = gs->curB->battleGetStackByID(ba.selectedStack, false); + if(ba.selectedStack >= 0) + parameters.aimToStack(gs->curB->battleGetStackByID(ba.selectedStack, false)); ESpellCastProblem::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h, s, ECastingMode::HERO_CASTING);//todo: should we check aimed cast(battleCanCastThisSpellHere)? if(escp != ESpellCastProblem::OK) @@ -4522,7 +4522,6 @@ void CGameHandler::stackTurnTrigger(const CStack * st) parameters.effectLevel = bonus->val;//todo: recheck parameters.aimToHex(BattleHex::INVALID); parameters.mode = ECastingMode::ENCHANTER_CASTING; - parameters.selectedStack = nullptr; spell->battleCast(spellEnv, parameters); @@ -5233,7 +5232,6 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta parameters.effectLevel = spellLevel; parameters.aimToStack(oneOfAttacked); parameters.mode = ECastingMode::AFTER_ATTACK_CASTING; - parameters.selectedStack = nullptr; spell->battleCast(spellEnv, parameters); } @@ -5263,7 +5261,6 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat ) parameters.aimToStack(gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked)); parameters.effectPower = power; parameters.mode = ECastingMode::AFTER_ATTACK_CASTING; - parameters.selectedStack = nullptr; spell->battleCast(this->spellEnv, parameters); }; @@ -5570,7 +5567,6 @@ void CGameHandler::runBattle() parameters.effectLevel = 3; parameters.aimToHex(BattleHex::INVALID); parameters.mode = ECastingMode::PASSIVE_CASTING; - parameters.selectedStack = nullptr; parameters.enchantPower = b->val; spell->battleCast(spellEnv, parameters); } From 838717dfc1cb10967f6200452b16d718b9c2c719 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 13:05:33 +0300 Subject: [PATCH 34/60] Magic mirror unified again with normal cast. --- lib/spells/CDefaultSpellMechanics.cpp | 192 ++++++++++++-------------- lib/spells/CDefaultSpellMechanics.h | 16 ++- 2 files changed, 99 insertions(+), 109 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index e15db46d6..4257559f2 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -118,19 +118,8 @@ namespace SRSLPraserHelpers } } -SpellCastContext::SpellCastContext(const DefaultSpellMechanics * mechanics_, const BattleSpellCastParameters & parameters): - mechanics(mechanics_), attackedCres(), sc(), si(), mode(parameters.mode) -{ - prepareBattleCast(parameters); - logGlobal->debugStream() << "Started spell cast. Spell: " << mechanics->owner->name << "; mode:" << mode; -} - -SpellCastContext::~SpellCastContext() -{ - logGlobal->debugStream() << "Finished spell cast. Spell: " << mechanics->owner->name << "; mode:" << mode; -} - -void SpellCastContext::prepareBattleCast(const BattleSpellCastParameters & parameters) +SpellCastContext::SpellCastContext(const DefaultSpellMechanics * mechanics_, const SpellCastEnvironment * env_, const BattleSpellCastParameters & parameters_): + mechanics(mechanics_), env(env_), attackedCres(), sc(), si(), parameters(parameters_), otherHero(nullptr), spellCost(0) { sc.side = parameters.casterSide; sc.id = mechanics->owner->id; @@ -140,6 +129,19 @@ void SpellCastContext::prepareBattleCast(const BattleSpellCastParameters & param sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING; sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1); sc.manaGained = 0; + + //check it there is opponent hero + const ui8 otherSide = 1-parameters.casterSide; + + if(parameters.cb->battleHasHero(otherSide)) + otherHero = parameters.cb->battleGetFightingHero(otherSide); + + logGlobal->debugStream() << "Started spell cast. Spell: " << mechanics->owner->name << "; mode:" << parameters.mode; +} + +SpellCastContext::~SpellCastContext() +{ + logGlobal->debugStream() << "Finished spell cast. Spell: " << mechanics->owner->name << "; mode:" << parameters.mode; } void SpellCastContext::addDamageToDisplay(const si32 value) @@ -152,7 +154,7 @@ void SpellCastContext::setDamageToDisplay(const si32 value) sc.dmgToDisplay = value; } -void SpellCastContext::sendCastPacket(const SpellCastEnvironment * env) +void SpellCastContext::sendCastPacket() { for(auto sta : attackedCres) { @@ -162,6 +164,70 @@ void SpellCastContext::sendCastPacket(const SpellCastEnvironment * env) env->sendAndApply(&sc); } +void SpellCastContext::beforeCast() +{ + //calculate spell cost + if(parameters.mode == ECastingMode::HERO_CASTING) + { + spellCost = parameters.cb->battleGetSpellCost(mechanics->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? + { + if(stack->owner == otherHero->tempOwner) + { + vstd::amax(manaChannel, stack->valOfBonuses(Bonus::MANA_CHANNELING)); + } + } + sc.manaGained = (manaChannel * spellCost) / 100; + } + + logGlobal->debugStream() << "spellCost: " << spellCost; + } +} + +void SpellCastContext::afterCast() +{ + sendCastPacket(); + + if(parameters.mode == ECastingMode::HERO_CASTING) + { + //spend mana + SetMana sm; + sm.absolute = false; + + sm.hid = parameters.casterHero->id; + sm.val = -spellCost; + + env->sendAndApply(&sm); + + if(sc.manaGained > 0) + { + assert(otherHero); + + sm.hid = otherHero->id; + sm.val = sc.manaGained; + env->sendAndApply(&sm); + } + } + else if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING) + { + //reduce number of casts remaining + assert(parameters.casterStack); + + BattleSetStackProperty ssp; + ssp.stackID = parameters.casterStack->ID; + ssp.which = BattleSetStackProperty::CASTS; + ssp.val = -1; + ssp.absolute = false; + env->sendAndApply(&ssp); + } + if(!si.stacks.empty()) //after spellcast info shows + env->sendAndApply(&si); +} + ///DefaultSpellMechanics void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { @@ -196,9 +262,9 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const B return; } - std::vector reflected;//for magic mirror + std::vector reflected, reflectedIgnore;//for magic mirror - castNormal(env, parameters, reflected); + cast(env, parameters, reflected); //Magic Mirror effect for(auto & attackedCre : reflected) @@ -214,7 +280,6 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const B int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position; BattleSpellCastParameters mirrorParameters(parameters.cb, attackedCre, owner); - mirrorParameters.spellLvl = 0; mirrorParameters.aimToHex(targetHex); mirrorParameters.mode = ECastingMode::MAGIC_MIRROR; mirrorParameters.spellLvl = parameters.spellLvl; @@ -222,41 +287,16 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const B mirrorParameters.effectPower = parameters.effectPower; mirrorParameters.effectValue = parameters.effectValue; mirrorParameters.enchantPower = parameters.enchantPower; - castMagicMirror(env, mirrorParameters); + cast(env, mirrorParameters, reflectedIgnore); } } } -void DefaultSpellMechanics::castNormal(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const +void DefaultSpellMechanics::cast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const { - SpellCastContext ctx(this, parameters); + SpellCastContext ctx(this, env, parameters); - //check it there is opponent hero - const ui8 otherSide = 1-parameters.casterSide; - const CGHeroInstance * otherHero = nullptr; - if(parameters.cb->battleHasHero(otherSide)) - otherHero = parameters.cb->battleGetFightingHero(otherSide); - int spellCost = 0; - - //calculate spell cost - if(parameters.mode == ECastingMode::HERO_CASTING) - { - 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? - { - if(stack->owner == otherHero->tempOwner) - { - vstd::amax(manaChannel, stack->valOfBonuses(Bonus::MANA_CHANNELING)); - } - } - ctx.sc.manaGained = (manaChannel * spellCost) / 100; - } - } - logGlobal->debugStream() << "spellCost: " << spellCost; + ctx.beforeCast(); ctx.attackedCres = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); @@ -264,66 +304,12 @@ void DefaultSpellMechanics::castNormal(const SpellCastEnvironment * env, const B handleResistance(env, ctx); - handleMagicMirror(env, ctx, reflected); + if(parameters.mode != ECastingMode::MAGIC_MIRROR) + handleMagicMirror(env, ctx, reflected); applyBattleEffects(env, parameters, ctx); - ctx.sendCastPacket(env); - - if(parameters.mode == ECastingMode::HERO_CASTING) - { - //spend mana - SetMana sm; - sm.absolute = false; - - sm.hid = parameters.casterHero->id; - sm.val = -spellCost; - - env->sendAndApply(&sm); - - if(ctx.sc.manaGained > 0) - { - assert(otherHero); - - sm.hid = otherHero->id; - sm.val = ctx.sc.manaGained; - env->sendAndApply(&sm); - } - } - else if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING) - { - //reduce number of casts remaining - assert(parameters.casterStack); - - BattleSetStackProperty ssp; - ssp.stackID = parameters.casterStack->ID; - ssp.which = BattleSetStackProperty::CASTS; - ssp.val = -1; - ssp.absolute = false; - env->sendAndApply(&ssp); - } - - if(!ctx.si.stacks.empty()) //after spellcast info shows - env->sendAndApply(&ctx.si); -} - -void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const -{ - SpellCastContext ctx(this, parameters); - - //calculating affected creatures for all spells - ctx.attackedCres = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.caster, parameters.spellLvl, parameters.getFirstDestinationHex()); - - logGlobal->debugStream() << "will affect: " << ctx.attackedCres.size() << " stacks"; - - handleResistance(env, ctx); - - applyBattleEffects(env, parameters, ctx); - - ctx.sendCastPacket(env); - - if(!ctx.si.stacks.empty()) //after spellcast info shows - env->sendAndApply(&ctx.si); + ctx.afterCast(); } void DefaultSpellMechanics::battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 2ce398314..3ec864f94 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -19,20 +19,25 @@ class DLL_LINKAGE SpellCastContext { public: const DefaultSpellMechanics * mechanics; + const SpellCastEnvironment * env; std::vector attackedCres;//must be vector, as in Chain Lightning order matters BattleSpellCast sc;//todo: make private StacksInjured si; - ECastingMode::ECastingMode mode; + const BattleSpellCastParameters & parameters; - SpellCastContext(const DefaultSpellMechanics * mechanics_, const BattleSpellCastParameters & parameters); + SpellCastContext(const DefaultSpellMechanics * mechanics_, const SpellCastEnvironment * env_, const BattleSpellCastParameters & parameters_); virtual ~SpellCastContext(); void addDamageToDisplay(const si32 value); void setDamageToDisplay(const si32 value); - void sendCastPacket(const SpellCastEnvironment * env); + void beforeCast(); + void afterCast(); private: - void prepareBattleCast(const BattleSpellCastParameters & parameters); + const CGHeroInstance * otherHero; + int spellCost; + + void sendCastPacket(); }; ///all combat spells @@ -65,8 +70,7 @@ protected: protected: void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const; private: - void castNormal(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const; - void castMagicMirror(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const; + void cast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector & reflected) const; void handleImmunities(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx, std::vector & stacks) const; void handleMagicMirror(const SpellCastEnvironment * env, SpellCastContext & ctx, std::vector & reflected) const; From 1ce4675df6d31f7d70c67e49fe88a84d922fdf57 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 13:33:11 +0300 Subject: [PATCH 35/60] Tweaks --- lib/spells/CDefaultSpellMechanics.cpp | 10 ++++++-- lib/spells/ISpellMechanics.cpp | 37 ++++++++++++++------------- lib/spells/ISpellMechanics.h | 8 +++--- server/CGameHandler.cpp | 12 ++++----- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 4257559f2..625c65ef1 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -262,13 +262,19 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const B return; } - std::vector reflected, reflectedIgnore;//for magic mirror + std::vector reflected;//for magic mirror cast(env, parameters, reflected); //Magic Mirror effect for(auto & attackedCre : reflected) { + if(parameters.mode == ECastingMode::MAGIC_MIRROR) + { + logGlobal->error("Magic mirror recurrence!"); + return; + } + TStacks mirrorTargets = parameters.cb->battleGetStacksIf([this, parameters](const CStack * battleStack) { //Get all enemy stacks. Magic mirror can reflect to immune creature (with no effect) @@ -287,7 +293,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const B mirrorParameters.effectPower = parameters.effectPower; mirrorParameters.effectValue = parameters.effectValue; mirrorParameters.enchantPower = parameters.enchantPower; - cast(env, mirrorParameters, reflectedIgnore); + mirrorParameters.cast(env); } } } diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 7843530fd..64c98d7e4 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -34,15 +34,26 @@ BattleSpellCastParameters::Destination::Destination(const BattleHex & destinatio } -BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell) - : cb(cb), caster(caster), casterColor(caster->getOwner()), casterSide(cb->whatSide(casterColor)), +BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell_) + : spell(spell_), cb(cb), caster(caster), casterColor(caster->getOwner()), casterSide(cb->whatSide(casterColor)), casterHero(nullptr), mode(ECastingMode::HERO_CASTING), casterStack(nullptr), spellLvl(-1), effectLevel(-1), effectPower(0), enchantPower(0), effectValue(0) { casterStack = dynamic_cast(caster); casterHero = dynamic_cast(caster); - prepare(spell); + + spellLvl = caster->getSpellSchoolLevel(spell); + effectLevel = caster->getEffectLevel(spell); + effectPower = caster->getEffectPower(spell); + effectValue = caster->getEffectValue(spell); + enchantPower = caster->getEnchantPower(spell); + + vstd::amax(spellLvl, 0); + vstd::amax(effectLevel, 0); + vstd::amax(enchantPower, 0); + vstd::amax(enchantPower, 0); + vstd::amax(effectValue, 0); } void BattleSpellCastParameters::aimToHex(const BattleHex& destination) @@ -58,26 +69,16 @@ void BattleSpellCastParameters::aimToStack(const CStack * destination) destinations.push_back(Destination(destination)); } +void BattleSpellCastParameters::cast(const SpellCastEnvironment * env) +{ + spell->battleCast(env, *this); +} + BattleHex BattleSpellCastParameters::getFirstDestinationHex() const { return destinations.at(0).hexValue; } -void BattleSpellCastParameters::prepare(const CSpell * spell) -{ - spellLvl = caster->getSpellSchoolLevel(spell); - effectLevel = caster->getEffectLevel(spell); - effectPower = caster->getEffectPower(spell); - effectValue = caster->getEffectValue(spell); - enchantPower = caster->getEnchantPower(spell); - - vstd::amax(spellLvl, 0); - vstd::amax(effectLevel, 0); - vstd::amax(enchantPower, 0); - vstd::amax(enchantPower, 0); - vstd::amax(effectValue, 0); -} - ///ISpellMechanics ISpellMechanics::ISpellMechanics(CSpell * s): owner(s) diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 4ece618c1..eb5d2bcbf 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -45,11 +45,15 @@ public: const BattleHex hexValue; }; - BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell); + BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell_); void aimToHex(const BattleHex & destination); void aimToStack(const CStack * destination); + + void cast(const SpellCastEnvironment * env); + BattleHex getFirstDestinationHex() const; + const CSpell * spell; const BattleInfo * cb; const ISpellCaster * caster; const PlayerColor casterColor; @@ -71,8 +75,6 @@ public: int enchantPower; ///for Archangel-like casting int effectValue; -private: - void prepare(const CSpell * spell); }; struct DLL_LINKAGE SpellTargetingContext diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 22d8f6a8c..62a89edd6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4171,7 +4171,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) parameters.effectLevel = parameters.spellLvl; parameters.mode = ECastingMode::CREATURE_ACTIVE_CASTING; parameters.aimToHex(destination);//todo: allow multiple destinations - spell->battleCast(spellEnv, parameters); + parameters.cast(spellEnv); } sendAndApply(&end_action); break; @@ -4382,7 +4382,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) StartAction start_action(ba); sendAndApply(&start_action); //start spell casting - s->battleCast(spellEnv, parameters); + parameters.cast(spellEnv); sendAndApply(&end_action); if( !gs->curB->battleGetStackByID(gs->curB->activeStack)) @@ -4523,7 +4523,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st) parameters.aimToHex(BattleHex::INVALID); parameters.mode = ECastingMode::ENCHANTER_CASTING; - spell->battleCast(spellEnv, parameters); + parameters.cast(spellEnv); //todo: move to mechanics BattleSetStackProperty ssp; @@ -5233,7 +5233,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta parameters.aimToStack(oneOfAttacked); parameters.mode = ECastingMode::AFTER_ATTACK_CASTING; - spell->battleCast(spellEnv, parameters); + parameters.cast(spellEnv); } } } @@ -5262,7 +5262,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat ) parameters.effectPower = power; parameters.mode = ECastingMode::AFTER_ATTACK_CASTING; - spell->battleCast(this->spellEnv, parameters); + parameters.cast(spellEnv); }; attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker); @@ -5568,7 +5568,7 @@ void CGameHandler::runBattle() parameters.aimToHex(BattleHex::INVALID); parameters.mode = ECastingMode::PASSIVE_CASTING; parameters.enchantPower = b->val; - spell->battleCast(spellEnv, parameters); + parameters.cast(spellEnv); } } } From f39e6193044048a497d06ab1168bcda8db9f8277 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Sep 2016 13:51:53 +0300 Subject: [PATCH 36/60] More tweaks --- lib/spells/CDefaultSpellMechanics.cpp | 8 +------- lib/spells/ISpellMechanics.cpp | 9 +++++++++ lib/spells/ISpellMechanics.h | 5 +++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 625c65ef1..088430a51 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -285,14 +285,8 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const B { int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position; - BattleSpellCastParameters mirrorParameters(parameters.cb, attackedCre, owner); + BattleSpellCastParameters mirrorParameters(parameters, attackedCre); mirrorParameters.aimToHex(targetHex); - mirrorParameters.mode = ECastingMode::MAGIC_MIRROR; - mirrorParameters.spellLvl = parameters.spellLvl; - mirrorParameters.effectLevel = parameters.effectLevel; - mirrorParameters.effectPower = parameters.effectPower; - mirrorParameters.effectValue = parameters.effectValue; - mirrorParameters.enchantPower = parameters.enchantPower; mirrorParameters.cast(env); } } diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 64c98d7e4..f3f7ef0d7 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -56,6 +56,15 @@ BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, cons vstd::amax(effectValue, 0); } +BattleSpellCastParameters::BattleSpellCastParameters(const BattleSpellCastParameters & orig, const ISpellCaster * caster) + :spell(orig.spell), cb(orig.cb), caster(caster), casterColor(caster->getOwner()), casterSide(cb->whatSide(casterColor)), + casterHero(nullptr), mode(ECastingMode::MAGIC_MIRROR), casterStack(nullptr), + spellLvl(orig.spellLvl), effectLevel(orig.effectLevel), effectPower(orig.effectPower), enchantPower(orig.enchantPower), effectValue(orig.effectValue) +{ + casterStack = dynamic_cast(caster); + casterHero = dynamic_cast(caster); +} + void BattleSpellCastParameters::aimToHex(const BattleHex& destination) { destinations.push_back(Destination(destination)); diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index eb5d2bcbf..811e8c740 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -45,7 +45,12 @@ public: const BattleHex hexValue; }; + //normal constructor BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell_); + + //magic mirror constructor + BattleSpellCastParameters(const BattleSpellCastParameters & orig, const ISpellCaster * caster); + void aimToHex(const BattleHex & destination); void aimToStack(const CStack * destination); From f8767a6380a711a06e261f1c50c4470df6dfbe8f Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 09:37:18 +0300 Subject: [PATCH 37/60] cleanup --- lib/spells/ISpellMechanics.cpp | 2 +- server/CGameHandler.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index f3f7ef0d7..fabe2ea4e 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -38,7 +38,7 @@ BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, cons : spell(spell_), cb(cb), caster(caster), casterColor(caster->getOwner()), casterSide(cb->whatSide(casterColor)), casterHero(nullptr), mode(ECastingMode::HERO_CASTING), casterStack(nullptr), - spellLvl(-1), effectLevel(-1), effectPower(0), enchantPower(0), effectValue(0) + spellLvl(0), effectLevel(0), effectPower(0), enchantPower(0), effectValue(0) { casterStack = dynamic_cast(caster); casterHero = dynamic_cast(caster); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 62a89edd6..718260906 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4109,7 +4109,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) bsa.creID = summonedType; ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum()); - ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;//todo: ignore AGE effect + ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount; ui64 canRiseHp = std::min(targetHealth, risedHp); ui32 canRiseAmount = canRiseHp / VLC->creh->creatures.at(bsa.creID)->MaxHealth(); From 62abde6c46f0b27b8d49e6a07d1d6ea6fc14fc5c Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 18:23:55 +0300 Subject: [PATCH 38/60] Prepare battle log for spell-cast on server side. --- client/battle/CBattleInterface.cpp | 9 +- lib/BattleState.cpp | 5 + lib/BattleState.h | 2 + lib/NetPacks.h | 6 +- lib/mapObjects/CGHeroInstance.cpp | 7 ++ lib/mapObjects/CGHeroInstance.h | 2 + lib/spells/CDefaultSpellMechanics.cpp | 143 ++++++++++++++++++-------- lib/spells/CDefaultSpellMechanics.h | 7 +- lib/spells/CSpellHandler.cpp | 53 ---------- lib/spells/CSpellHandler.h | 4 - lib/spells/CreatureSpellMechanics.cpp | 6 +- lib/spells/ISpellMechanics.h | 3 - lib/spells/Magic.h | 3 + 13 files changed, 132 insertions(+), 118 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 4a84ac176..c4e93477c 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1323,12 +1323,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) } //displaying message in console - std::vector logLines; - - spell.prepareBattleLog(curInt->cb.get(), sc, logLines); - - for(auto line : logLines) - console->addText(line); + for(const auto & line : sc->battleLog) + if(!console->addText(line.toString())) + logGlobal->warn("Too long battle log line"); waitForAnims(); //mana absorption diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 7a8371312..1af318ea7 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -1243,6 +1243,11 @@ const PlayerColor CStack::getOwner() const return owner; } +void CStack::getCasterName(MetaString & text) const +{ + //always plural name in case of spell cast. + text.addReplacement(MetaString::CRE_PL_NAMES, type->idNumber.num); +} bool CMP_stack::operator()( const CStack* a, const CStack* b ) { diff --git a/lib/BattleState.h b/lib/BattleState.h index 31db8f997..26b70b7d5 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -249,6 +249,8 @@ public: const PlayerColor getOwner() const override; + void getCasterName(MetaString & text) const override; + ///stack will be ghost in next battle state update void makeGhost(); diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 58d9b34bb..ef0e8ca7e 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1492,7 +1492,6 @@ struct BattleSpellCast : public CPackForClient//3009 DLL_LINKAGE void applyGs(CGameState *gs); void applyCl(CClient *cl); - si32 dmgToDisplay; //this amount will be displayed as amount of damage dealt by spell ui8 side; //which hero did cast spell: 0 - attacker, 1 - defender ui32 id; //id of spell ui8 skill; //caster's skill level @@ -1502,9 +1501,12 @@ struct BattleSpellCast : public CPackForClient//3009 std::set affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure) si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID bool castByHero; //if true - spell has been cast by hero, otherwise by a creature + + std::vector battleLog; template void serialize(Handler &h, const int version) { - h & dmgToDisplay & side & id & skill & manaGained & tile & customEffects & affectedCres & casterStack & castByHero; + h & side & id & skill & manaGained & tile & customEffects & affectedCres & casterStack & castByHero; + h & battleLog; } }; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index e66a62677..2f366740e 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -953,6 +953,13 @@ const PlayerColor CGHeroInstance::getOwner() const return tempOwner; } +void CGHeroInstance::getCasterName(MetaString & text) const +{ + //FIXME: use local name, MetaString need access to gamestate as hero name is part of map object + + text.addReplacement(name); +} + bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const { if(nullptr == getArt(ArtifactPosition::SPELLBOOK)) diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index fb8c1256c..9922ffcbd 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -246,6 +246,8 @@ public: const PlayerColor getOwner() const override; + void getCasterName(MetaString & text) const override; + void deserializationFix(); void initObj() override; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 088430a51..aa8c703ef 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -119,13 +119,12 @@ namespace SRSLPraserHelpers } SpellCastContext::SpellCastContext(const DefaultSpellMechanics * mechanics_, const SpellCastEnvironment * env_, const BattleSpellCastParameters & parameters_): - mechanics(mechanics_), env(env_), attackedCres(), sc(), si(), parameters(parameters_), otherHero(nullptr), spellCost(0) + mechanics(mechanics_), env(env_), attackedCres(), sc(), si(), parameters(parameters_), otherHero(nullptr), spellCost(0), damageToDisplay(0) { sc.side = parameters.casterSide; sc.id = mechanics->owner->id; sc.skill = parameters.spellLvl; sc.tile = parameters.getFirstDestinationHex(); - sc.dmgToDisplay = 0; sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING; sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1); sc.manaGained = 0; @@ -146,22 +145,63 @@ SpellCastContext::~SpellCastContext() void SpellCastContext::addDamageToDisplay(const si32 value) { - sc.dmgToDisplay += value; + damageToDisplay += value; } void SpellCastContext::setDamageToDisplay(const si32 value) { - sc.dmgToDisplay = value; + damageToDisplay = value; } -void SpellCastContext::sendCastPacket() +void SpellCastContext::prepareBattleLog() { - for(auto sta : attackedCres) + //todo: prepare battle log + bool displayDamage = true; + + if(attackedCres.size() == 1) { - sc.affectedCres.insert(sta->ID); + const CStack * attackedStack = *attackedCres.begin(); + + switch(parameters.mode) + { + case ECastingMode::HERO_CASTING: + { + MetaString line; + line.addTxt(MetaString::GENERAL_TXT, 195); + parameters.caster->getCasterName(line); + line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum()); + line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num); + } + break; + + default: + { + mechanics->battleLogSingleTarget(sc.battleLog, parameters, attackedStack, damageToDisplay, displayDamage); + } + break; + } + } + else + { + MetaString line; + line.addTxt(MetaString::GENERAL_TXT, 196); + parameters.caster->getCasterName(line); + line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum()); + sc.battleLog.push_back(line); } - env->sendAndApply(&sc); + displayDamage = displayDamage && damageToDisplay > 0; + + if(displayDamage) + { + MetaString line; + + line.addTxt(MetaString::GENERAL_TXT, 376); + line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum()); + line.addReplacement(damageToDisplay); + + sc.battleLog.push_back(line); + } } void SpellCastContext::beforeCast() @@ -190,7 +230,14 @@ void SpellCastContext::beforeCast() void SpellCastContext::afterCast() { - sendCastPacket(); + for(auto sta : attackedCres) + { + sc.affectedCres.insert(sta->ID); + } + + prepareBattleLog(); + + env->sendAndApply(&sc); if(parameters.mode == ECastingMode::HERO_CASTING) { @@ -312,33 +359,32 @@ void DefaultSpellMechanics::cast(const SpellCastEnvironment * env, const BattleS ctx.afterCast(); } -void DefaultSpellMechanics::battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, - const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const +void DefaultSpellMechanics::battleLogSingleTarget(std::vector& logLines, const BattleSpellCastParameters & parameters, + const CStack * attackedStack, const si32 damageToDisplay, 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 + auto getPluralFormat = [attackedStack](const int baseTextID) -> si32 { - return boost::format(VLC->generaltexth->allTexts[(attackedStack->count > 1 ? baseTextID + 1 : baseTextID)]); + return attackedStack->count > 1 ? baseTextID + 1 : baseTextID; }; - auto logSimple = [&logLines, getPluralFormat, attackedName](const int baseTextID) + auto logSimple = [attackedStack, &logLines, getPluralFormat](const int baseTextID) { - boost::format fmt = getPluralFormat(baseTextID); - fmt % attackedName; - logLines.push_back(fmt.str()); + MetaString line; + line.addTxt(MetaString::GENERAL_TXT, getPluralFormat(baseTextID)); + line.addReplacement(*attackedStack); + logLines.push_back(line); }; - auto logPlural = [&logLines, attackedNamePl](const int baseTextID) + auto logPlural = [attackedStack, &logLines, getPluralFormat](const int baseTextID) { - boost::format fmt(VLC->generaltexth->allTexts[baseTextID]); - fmt % attackedNamePl; - logLines.push_back(fmt.str()); + MetaString line; + line.addTxt(MetaString::GENERAL_TXT, baseTextID); + line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num); + logLines.push_back(line); }; displayDamage = false; //in most following cases damage info text is custom + switch(owner->id) { case SpellID::STONE_GAZE: @@ -358,52 +404,61 @@ void DefaultSpellMechanics::battleLogSingleTarget(std::vector & log break; case SpellID::AGE: { - boost::format text = getPluralFormat(551); - text % attackedName; //The %s shrivel with age, and lose %d hit points." + MetaString line; + line.addTxt(MetaString::GENERAL_TXT, getPluralFormat(551)); + line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num); + + //todo: display effect from only this cast TBonusListPtr bl = attackedStack->getBonuses(Selector::type(Bonus::STACK_HEALTH)); const int fullHP = bl->totalValue(); bl->remove_if(Selector::source(Bonus::SPELL_EFFECT, SpellID::AGE)); - text % (fullHP - bl->totalValue()); - logLines.push_back(text.str()); + line.addReplacement(fullHP - bl->totalValue()); + logLines.push_back(line); } break; case SpellID::THUNDERBOLT: { logPlural(367); + MetaString line; + //todo: handle newlines in metastring std::string text = VLC->generaltexth->allTexts[343].substr(1, VLC->generaltexth->allTexts[343].size() - 1); //Does %d points of damage. - boost::algorithm::replace_first(text, "%d", boost::lexical_cast(packet->dmgToDisplay)); //no more text afterwards - logLines.push_back(text); + line << text; + line.addReplacement(damageToDisplay); //no more text afterwards + logLines.push_back(line); } break; case SpellID::DISPEL_HELPFUL_SPELLS: logPlural(555); break; case SpellID::DEATH_STARE: - if (packet->dmgToDisplay > 0) + if (damageToDisplay > 0) { - std::string text; - if (packet->dmgToDisplay > 1) + MetaString line; + if (damageToDisplay > 1) { - text = VLC->generaltexth->allTexts[119]; //%d %s die under the terrible gaze of the %s. - boost::algorithm::replace_first(text, "%d", boost::lexical_cast(packet->dmgToDisplay)); - boost::algorithm::replace_first(text, "%s", attackedNamePl); + line.addTxt(MetaString::GENERAL_TXT, 119); //%d %s die under the terrible gaze of the %s. + line.addReplacement(damageToDisplay); + line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num); } else { - text = VLC->generaltexth->allTexts[118]; //One %s dies under the terrible gaze of the %s. - boost::algorithm::replace_first(text, "%s", attackedNameSing); + line.addTxt(MetaString::GENERAL_TXT, 118); //One %s dies under the terrible gaze of the %s. + line.addReplacement(MetaString::CRE_SING_NAMES, attackedStack->getCreature()->idNumber.num); } - boost::algorithm::replace_first(text, "%s", casterName); //casting stack - logLines.push_back(text); + parameters.caster->getCasterName(line); + logLines.push_back(line); } break; default: { - boost::format text(VLC->generaltexth->allTexts[565]); //The %s casts %s - text % casterName % owner->name; + MetaString line; + line.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s + //todo: use text 566 for single creature + parameters.caster->getCasterName(line); + line.addReplacement(MetaString::SPELL_NAME, owner->id.toEnum()); displayDamage = true; - logLines.push_back(text.str()); + logLines.push_back(line); } break; } diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 3ec864f94..1103c531b 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -36,8 +36,9 @@ public: private: const CGHeroInstance * otherHero; int spellCost; + si32 damageToDisplay; - void sendCastPacket(); + void prepareBattleLog(); }; ///all combat spells @@ -58,8 +59,8 @@ public: void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const override final; - void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, - const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override; + void battleLogSingleTarget(std::vector & logLines, const BattleSpellCastParameters & parameters, + const CStack * attackedStack, const si32 damageToDisplay, bool & displayDamage) const; bool requiresCreatureTarget() const override; protected: diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 0c9767771..f85594c79 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -586,59 +586,6 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const ISpellCaster return ESpellCastProblem::OK; } -void CSpell::prepareBattleLog(const CBattleInfoCallback * cb, const BattleSpellCast * packet, std::vector & logLines) const -{ - bool displayDamage = true; - - std::string casterName("Something"); //todo: localize - - if(packet->castByHero) - casterName = cb->battleGetHeroInfo(packet->side).name; - - { - const auto casterStackID = packet->casterStack; - - if(casterStackID > 0) - { - const CStack * casterStack = cb->battleGetStackByID(casterStackID); - if(casterStack != nullptr) - casterName = casterStack->type->namePl; - } - } - - if(packet->affectedCres.size() == 1) - { - const CStack * attackedStack = cb->battleGetStackByID(*packet->affectedCres.begin(), false); - - const std::string attackedNamePl = attackedStack->getCreature()->namePl; - - if(packet->castByHero) - { - const std::string fmt = VLC->generaltexth->allTexts[195]; - logLines.push_back(boost::to_string(boost::format(fmt) % casterName % this->name % attackedNamePl)); - } - else - { - mechanics->battleLogSingleTarget(logLines, packet, casterName, attackedStack, displayDamage); - } - } - else - { - boost::format text(VLC->generaltexth->allTexts[196]); - text % casterName % this->name; - logLines.push_back(text.str()); - } - - - if(packet->dmgToDisplay > 0 && displayDamage) - { - boost::format dmgInfo(VLC->generaltexth->allTexts[376]); - dmgInfo % this->name % packet->dmgToDisplay; - logLines.push_back(dmgInfo.str()); - } -} - - void CSpell::setIsOffensive(const bool val) { isOffensive = val; diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 30463b8ef..aeb7c2c1a 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -287,10 +287,6 @@ public: ///implementation of BattleSpellCast applying void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const; - -public://Client logic. - void prepareBattleLog(const CBattleInfoCallback * cb, const BattleSpellCast * packet, std::vector & logLines) const; - public://internal, for use only by Mechanics classes ///applies caster`s secondary skills and affectedCreature`s to raw damage int adjustRawDamage(const ISpellCaster * caster, const CStack * affectedCreature, int rawDamage) const; diff --git a/lib/spells/CreatureSpellMechanics.cpp b/lib/spells/CreatureSpellMechanics.cpp index eba0ba447..7c545c7be 100644 --- a/lib/spells/CreatureSpellMechanics.cpp +++ b/lib/spells/CreatureSpellMechanics.cpp @@ -58,12 +58,12 @@ ESpellCastProblem::ESpellCastProblem AcidBreathDamageMechanics::isImmuneByStack( void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { //calculating dmg to display - si32 dmgToDisplay = parameters.effectPower; + si32 damageToDisplay = parameters.effectPower; if(!ctx.attackedCres.empty()) - vstd::amin(dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack + vstd::amin(damageToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack - ctx.setDamageToDisplay(dmgToDisplay); + ctx.setDamageToDisplay(damageToDisplay); for(auto & attackedCre : ctx.attackedCres) { diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 811e8c740..6636c836e 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -114,9 +114,6 @@ public: virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0; virtual void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const = 0; - virtual void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, - const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0; - //if true use generic algorithm for target existence check, see CSpell::canBeCast virtual bool requiresCreatureTarget() const = 0; diff --git a/lib/spells/Magic.h b/lib/spells/Magic.h index 5055fef35..0d93047db 100644 --- a/lib/spells/Magic.h +++ b/lib/spells/Magic.h @@ -18,6 +18,7 @@ class CSpell; class CStack; class PlayerColor; +struct MetaString; class DLL_LINKAGE ISpellCaster { @@ -45,4 +46,6 @@ public: virtual int getEffectValue(const CSpell * spell) const = 0; virtual const PlayerColor getOwner() const = 0; + + virtual void getCasterName(MetaString & text) const = 0; }; From be002f6576fbe06a3d4fb649876ac6abd610027a Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 18:56:38 +0300 Subject: [PATCH 39/60] Cleanup --- client/battle/CBattleInterface.cpp | 21 ++++++++++----------- client/battle/CBattleInterface.h | 10 +++++----- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index c4e93477c..1bfe548d2 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1272,7 +1272,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) } //todo: play custom cast animation - displaySpellCast(spellID, BattleHex::INVALID, false); + displaySpellCast(spellID, BattleHex::INVALID); //playing projectile animation if(sc->tile.isValid()) @@ -1415,13 +1415,12 @@ void CBattleInterface::castThisSpell(SpellID spellID) } } -void CBattleInterface::displayEffect(ui32 effect, int destTile, bool areaEffect) +void CBattleInterface::displayEffect(ui32 effect, int destTile) { - //todo: recheck areaEffect usage addNewAnim(new CSpellEffectAnimation(this, effect, destTile, 0, 0, false)); } -void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect) +void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile) { if(animation.pause > 0) { @@ -1433,7 +1432,7 @@ void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animatio } } -void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect) +void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTile) { const CSpell * spell = spellID.toSpell(); @@ -1442,11 +1441,11 @@ void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTi for(const CSpell::TAnimation & animation : spell->animationInfo.cast) { - displaySpellAnimation(animation, destinationTile, areaEffect); + displaySpellAnimation(animation, destinationTile); } } -void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect) +void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile) { const CSpell * spell = spellID.toSpell(); @@ -1455,11 +1454,11 @@ void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destination for(const CSpell::TAnimation & animation : spell->animationInfo.affect) { - displaySpellAnimation(animation, destinationTile, areaEffect); + displaySpellAnimation(animation, destinationTile); } } -void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect) +void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTile) { const CSpell * spell = spellID.toSpell(); @@ -1468,7 +1467,7 @@ void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTil for(const CSpell::TAnimation & animation : spell->animationInfo.hit) { - displaySpellAnimation(animation, destinationTile, areaEffect); + displaySpellAnimation(animation, destinationTile); } } @@ -1633,7 +1632,7 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack) for (Bonus * spellBonus : spellBonuses) { spell = CGI->spellh->objects[spellBonus->subtype]; - switch (spellBonus->subtype) + switch (spell->id) { case SpellID::REMOVE_OBSTACLE: possibleActions.push_back (OBSTACLE); diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index 7b9473c21..8b9d82dca 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -334,13 +334,13 @@ public: void spellCast(const BattleSpellCast * sc); //called when a hero casts a spell void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook - void displayEffect(ui32 effect, int destTile, bool areaEffect = true); //displays custom effect on the battlefield + void displayEffect(ui32 effect, int destTile); //displays custom effect on the battlefield - void displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s cast animation - void displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation - void displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation + void displaySpellCast(SpellID spellID, BattleHex destinationTile); //displays spell`s cast animation + void displaySpellEffect(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation + void displaySpellHit(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation - void displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect = true); + void displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile); void battleTriggerEffect(const BattleTriggerEffect & bte); void setBattleCursor(const int myNumber); //really complex and messy, sets attackingHex From b7509f588ffaf0bba7187afb0a9a9f2c0617cf1a Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 19:26:55 +0300 Subject: [PATCH 40/60] Support all spell target types (except NO_TARGET) for creature casting. --- client/battle/CBattleInterface.cpp | 62 ++++++++++++++++-------------- client/battle/CBattleInterface.h | 1 + 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 1bfe548d2..40aff2075 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1366,6 +1366,30 @@ void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse) } } +CBattleInterface::PossibleActions CBattleInterface::getCasterAction(const CSpell * spell, const ISpellCaster * caster) const +{ + PossibleActions spellSelMode = ANY_LOCATION; + + const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell)); + + if(ti.massive || ti.type == CSpell::NO_TARGET) + spellSelMode = NO_LOCATION; + else if(ti.type == CSpell::LOCATION && ti.clearAffected) + { + spellSelMode = FREE_LOCATION; + } + else if(ti.type == CSpell::CREATURE) + { + spellSelMode = AIMED_SPELL_CREATURE; + } + else if(ti.type == CSpell::OBSTACLE) + { + spellSelMode = OBSTACLE; + } + + return spellSelMode; +} + void CBattleInterface::castThisSpell(SpellID spellID) { auto ba = new BattleAction; @@ -1382,24 +1406,7 @@ void CBattleInterface::castThisSpell(SpellID spellID) const CGHeroInstance * castingHero = (attackingHeroInstance->tempOwner == curInt->playerID) ? attackingHeroInstance : defendingHeroInstance; assert(castingHero); // code below assumes non-null hero sp = spellID.toSpell(); - PossibleActions spellSelMode = ANY_LOCATION; - - const CSpell::TargetInfo ti(sp, castingHero->getSpellSchoolLevel(sp)); - - if(ti.massive || ti.type == CSpell::NO_TARGET) - spellSelMode = NO_LOCATION; - else if(ti.type == CSpell::LOCATION && ti.clearAffected) - { - spellSelMode = FREE_LOCATION; - } - else if(ti.type == CSpell::CREATURE) - { - spellSelMode = AIMED_SPELL_CREATURE; - } - else if(ti.type == CSpell::OBSTACLE) - { - spellSelMode = OBSTACLE; - } + PossibleActions spellSelMode = getCasterAction(sp, castingHero); if (spellSelMode == NO_LOCATION) //user does not have to select location { @@ -1631,17 +1638,14 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack) BonusList spellBonuses = *stack->getBonuses (Selector::type(Bonus::SPELLCASTER)); for (Bonus * spellBonus : spellBonuses) { - spell = CGI->spellh->objects[spellBonus->subtype]; - switch (spell->id) - { - case SpellID::REMOVE_OBSTACLE: - possibleActions.push_back (OBSTACLE); - break; - default: - possibleActions.push_back (AIMED_SPELL_CREATURE); - break; - } + spell = SpellID(spellBonus->subtype).toSpell(); + PossibleActions act = getCasterAction(spell, stack); + + if(act == NO_LOCATION) + logGlobal->error("NO_LOCATION action target is not yet supported for creatures"); + else + possibleActions.push_back(act); } std::sort(possibleActions.begin(), possibleActions.end()); auto it = std::unique (possibleActions.begin(), possibleActions.end()); @@ -2432,7 +2436,7 @@ bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CSta if (sp) { - const ISpellCaster * caster = creatureCasting ? dynamic_cast(sactive) : dynamic_cast(curInt->cb->battleGetMyHero()); + const ISpellCaster * caster = creatureCasting ? static_cast(sactive) : static_cast(curInt->cb->battleGetMyHero()); if(caster == nullptr) { isCastingPossible = false;//just in case diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index 8b9d82dca..803350ee8 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -259,6 +259,7 @@ private: void redrawBackgroundWithHexes(const CStack * activeStack); /** End of battle screen blitting methods */ + PossibleActions getCasterAction(const CSpell * spell, const ISpellCaster * caster) const; public: std::list > pendingAnims; //currently displayed animations void addNewAnim(CBattleAnimation * anim); //adds new anim to pendingAnims From f36b40e31192abfd52873c7b14544dc0bf632152 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 19:38:14 +0300 Subject: [PATCH 41/60] fixed typo --- client/battle/CBattleInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 40aff2075..e6681e7fe 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1402,7 +1402,7 @@ void CBattleInterface::castThisSpell(SpellID spellID) spellDestSelectMode = true; creatureCasting = false; - //choosing possible tragets + //choosing possible targets const CGHeroInstance * castingHero = (attackingHeroInstance->tempOwner == curInt->playerID) ? attackingHeroInstance : defendingHeroInstance; assert(castingHero); // code below assumes non-null hero sp = spellID.toSpell(); From 8eca149eb3ba25d471e34d5d0a4eae67cfc9cc59 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 20:21:51 +0300 Subject: [PATCH 42/60] Fixed creature`s aimed spell targeting. --- client/battle/CBattleInterface.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index e6681e7fe..e6840048d 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1586,8 +1586,8 @@ void CBattleInterface::activateStack() stackCanCastSpell = true; if(randomSpellcaster) creatureSpellToCast = -1; //spell will be set later on cast - - creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move + else + creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move //TODO: what if creature can cast BOTH random genie spell and aimed spell? } else @@ -1633,23 +1633,16 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack) { if (stack->hasBonusOfType (Bonus::SPELLCASTER)) { - //TODO: poll possible spells - const CSpell * spell; - BonusList spellBonuses = *stack->getBonuses (Selector::type(Bonus::SPELLCASTER)); - for (Bonus * spellBonus : spellBonuses) + if(creatureSpellToCast != -1) { - spell = SpellID(spellBonus->subtype).toSpell(); - + const CSpell * spell = SpellID(creatureSpellToCast).toSpell(); PossibleActions act = getCasterAction(spell, stack); - if(act == NO_LOCATION) logGlobal->error("NO_LOCATION action target is not yet supported for creatures"); else possibleActions.push_back(act); + } - std::sort(possibleActions.begin(), possibleActions.end()); - auto it = std::unique (possibleActions.begin(), possibleActions.end()); - possibleActions.erase (it, possibleActions.end()); } if (stack->hasBonusOfType (Bonus::RANDOM_SPELLCASTER)) possibleActions.push_back (RANDOM_GENIE_SPELL); From 4a9978c6423f1682bf3af36ed6f58747968ac455 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 21:07:36 +0300 Subject: [PATCH 43/60] Disabled massive spells fore creatures as they are not fully supported yet. * (WoG) commanders now cast spells properly. --- client/battle/CBattleInterface.cpp | 10 +++++----- client/battle/CBattleInterface.h | 2 +- lib/spells/CSpellHandler.cpp | 4 ++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index e6840048d..88a238e3a 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1366,11 +1366,11 @@ void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse) } } -CBattleInterface::PossibleActions CBattleInterface::getCasterAction(const CSpell * spell, const ISpellCaster * caster) const +CBattleInterface::PossibleActions CBattleInterface::getCasterAction(const CSpell * spell, const ISpellCaster * caster, ECastingMode::ECastingMode mode) const { PossibleActions spellSelMode = ANY_LOCATION; - const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell)); + const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell), mode); if(ti.massive || ti.type == CSpell::NO_TARGET) spellSelMode = NO_LOCATION; @@ -1406,7 +1406,7 @@ void CBattleInterface::castThisSpell(SpellID spellID) const CGHeroInstance * castingHero = (attackingHeroInstance->tempOwner == curInt->playerID) ? attackingHeroInstance : defendingHeroInstance; assert(castingHero); // code below assumes non-null hero sp = spellID.toSpell(); - PossibleActions spellSelMode = getCasterAction(sp, castingHero); + PossibleActions spellSelMode = getCasterAction(sp, castingHero, ECastingMode::HERO_CASTING); if (spellSelMode == NO_LOCATION) //user does not have to select location { @@ -1589,6 +1589,7 @@ void CBattleInterface::activateStack() else creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move //TODO: what if creature can cast BOTH random genie spell and aimed spell? + //TODO: faerie dragon type spell should be selected by server } else { @@ -1636,12 +1637,11 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack) if(creatureSpellToCast != -1) { const CSpell * spell = SpellID(creatureSpellToCast).toSpell(); - PossibleActions act = getCasterAction(spell, stack); + PossibleActions act = getCasterAction(spell, stack, ECastingMode::CREATURE_ACTIVE_CASTING); if(act == NO_LOCATION) logGlobal->error("NO_LOCATION action target is not yet supported for creatures"); else possibleActions.push_back(act); - } } if (stack->hasBonusOfType (Bonus::RANDOM_SPELLCASTER)) diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index 803350ee8..7a700d202 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -259,7 +259,7 @@ private: void redrawBackgroundWithHexes(const CStack * activeStack); /** End of battle screen blitting methods */ - PossibleActions getCasterAction(const CSpell * spell, const ISpellCaster * caster) const; + PossibleActions getCasterAction(const CSpell * spell, const ISpellCaster * caster, ECastingMode::ECastingMode mode) const; public: std::list > pendingAnims; //currently displayed animations void addNewAnim(CBattleAnimation * anim); //adds new anim to pendingAnims diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index f85594c79..16c601c3e 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -672,6 +672,10 @@ CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level, ECastingMo { alwaysHitDirectly = true; } + else if(mode == ECastingMode::CREATURE_ACTIVE_CASTING) + { + massive = false;//FIXME: find better solution for Commander spells + } } void CSpell::TargetInfo::init(const CSpell * spell, const int level) From 5e0a9d7966183bc6859d2eb0603ca40c1608ef16 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 22:41:50 +0300 Subject: [PATCH 44/60] hackfix for commander spells --- lib/spells/BattleSpellMechanics.cpp | 2 +- lib/spells/CDefaultSpellMechanics.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 486b9900f..dbf0b7419 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -731,7 +731,7 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(cons { const bool ownerMatches = !ctx.ti.smart || s->getOwner() == ctx.caster->getOwner(); - return ownerMatches && s->isValidTarget(true) && s->coversPos(ctx.destination); + return ownerMatches && s->isValidTarget(!ctx.ti.onlyAlive) && s->coversPos(ctx.destination); }); if(nullptr == stack) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index aa8c703ef..60d716ec0 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -664,7 +664,11 @@ std::vector DefaultSpellMechanics::calculateAffectedStacks(const std::set attackedCres;//std::set to exclude multiple occurrences of two hex creatures const ui8 attackerSide = cb->playerToSide(ctx.caster->getOwner()) == 1; - const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide); + auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide); + + //hackfix for banned creature massive spells + if(!ctx.ti.massive && owner->getLevelInfo(ctx.schoolLvl).range == "X") + attackedHexes.push_back(ctx.destination); auto mainFilter = [=](const CStack * s) { From 3fa62beb6d3fde2b63f1fa5d0ebcb38bb2672043 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 11 Sep 2016 13:48:14 +0300 Subject: [PATCH 45/60] Do not allow to cast Cure if there is nothing to cure. --- lib/spells/BattleSpellMechanics.cpp | 30 ++++++++++++++++++++--------- lib/spells/BattleSpellMechanics.h | 3 +++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index dbf0b7419..778735920 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -172,15 +172,8 @@ ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const ISpel void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { DefaultSpellMechanics::applyBattle(battle, packet); - doDispell(battle, packet, [](const Bonus * b) -> bool - { - if(b->source == Bonus::SPELL_EFFECT) - { - CSpell * sp = SpellID(b->sid).toSpell(); - return sp->isNegative(); - } - return false; //not a spell effect - }); + + doDispell(battle, packet, dispellSelector); } HealingSpellMechanics::EHealLevel CureMechanics::getHealLevel(int effectLevel) const @@ -188,6 +181,25 @@ HealingSpellMechanics::EHealLevel CureMechanics::getHealLevel(int effectLevel) c return EHealLevel::HEAL; } +bool CureMechanics::dispellSelector(const Bonus * b) +{ + if(b->source == Bonus::SPELL_EFFECT) + { + CSpell * sp = SpellID(b->sid).toSpell(); + return sp->isNegative(); + } + return false; //not a spell effect +} + +ESpellCastProblem::ESpellCastProblem CureMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const +{ + //Selector method name is ok as cashing string. --AVS + if(!obj->canBeHealed() && !obj->hasBonus(dispellSelector, "CureMechanics::dispellSelector")) + return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; + + return DefaultSpellMechanics::isImmuneByStack(caster, obj); +} + ///DispellMechanics void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index dadfa36b4..8c2064f42 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -63,8 +63,11 @@ public: CureMechanics(CSpell * s): HealingSpellMechanics(s){}; void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final; + ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; EHealLevel getHealLevel(int effectLevel) const override final; +private: + static bool dispellSelector(const Bonus * b); }; class DLL_LINKAGE DispellMechanics : public DefaultSpellMechanics From 53fbf88316f49860dc8a8c8f0e6b31a41c603f58 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 17 Sep 2016 23:04:23 +0300 Subject: [PATCH 46/60] Spell cast logging refactored. --- lib/BattleState.cpp | 8 ++++ lib/BattleState.h | 2 + lib/mapObjects/CGHeroInstance.cpp | 12 +++++ lib/mapObjects/CGHeroInstance.h | 1 + lib/spells/CDefaultSpellMechanics.cpp | 64 +++++++++------------------ lib/spells/CDefaultSpellMechanics.h | 7 ++- lib/spells/Magic.h | 4 ++ 7 files changed, 53 insertions(+), 45 deletions(-) diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 1af318ea7..b1c874d8b 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -1249,6 +1249,14 @@ void CStack::getCasterName(MetaString & text) const text.addReplacement(MetaString::CRE_PL_NAMES, type->idNumber.num); } +void CStack::getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const +{ + text.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s + //todo: use text 566 for single creature + getCasterName(text); + text.addReplacement(MetaString::SPELL_NAME, spell->id.toEnum()); +} + bool CMP_stack::operator()( const CStack* a, const CStack* b ) { switch(phase) diff --git a/lib/BattleState.h b/lib/BattleState.h index 26b70b7d5..6c6a998b3 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -251,6 +251,8 @@ public: void getCasterName(MetaString & text) const override; + void getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const override; + ///stack will be ghost in next battle state update void makeGhost(); diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 22048214a..dd35a1f98 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -960,6 +960,18 @@ void CGHeroInstance::getCasterName(MetaString & text) const text.addReplacement(name); } +void CGHeroInstance::getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const +{ + const bool singleTarget = attacked.size() == 1; + const int textIndex = singleTarget ? 195 : 196; + + text.addTxt(MetaString::GENERAL_TXT, textIndex); + getCasterName(text); + text.addReplacement(MetaString::SPELL_NAME, spell->id.toEnum()); + if(singleTarget) + text.addReplacement(MetaString::CRE_PL_NAMES, attacked.at(0)->getCreature()->idNumber.num); +} + bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const { if(nullptr == getArt(ArtifactPosition::SPELLBOOK)) diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index aed02a5c6..dce645af4 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -247,6 +247,7 @@ public: const PlayerColor getOwner() const override; void getCasterName(MetaString & text) const override; + void getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const override; void deserializationFix(); diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 60d716ec0..a8fe43de0 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -155,40 +155,9 @@ void SpellCastContext::setDamageToDisplay(const si32 value) void SpellCastContext::prepareBattleLog() { - //todo: prepare battle log bool displayDamage = true; - if(attackedCres.size() == 1) - { - const CStack * attackedStack = *attackedCres.begin(); - - switch(parameters.mode) - { - case ECastingMode::HERO_CASTING: - { - MetaString line; - line.addTxt(MetaString::GENERAL_TXT, 195); - parameters.caster->getCasterName(line); - line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum()); - line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num); - } - break; - - default: - { - mechanics->battleLogSingleTarget(sc.battleLog, parameters, attackedStack, damageToDisplay, displayDamage); - } - break; - } - } - else - { - MetaString line; - line.addTxt(MetaString::GENERAL_TXT, 196); - parameters.caster->getCasterName(line); - line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum()); - sc.battleLog.push_back(line); - } + mechanics->battleLog(sc.battleLog, parameters, attackedCres, damageToDisplay, displayDamage); displayDamage = displayDamage && damageToDisplay > 0; @@ -359,9 +328,18 @@ void DefaultSpellMechanics::cast(const SpellCastEnvironment * env, const BattleS ctx.afterCast(); } -void DefaultSpellMechanics::battleLogSingleTarget(std::vector& logLines, const BattleSpellCastParameters & parameters, - const CStack * attackedStack, const si32 damageToDisplay, bool & displayDamage) const +void DefaultSpellMechanics::battleLog(std::vector & logLines, const BattleSpellCastParameters & parameters, + const std::vector & attacked, const si32 damageToDisplay, bool & displayDamage) const { + if(attacked.size() != 1) + { + displayDamage = true; + battleLogDefault(logLines, parameters, attacked); + return; + } + + auto attackedStack = attacked.at(0); + auto getPluralFormat = [attackedStack](const int baseTextID) -> si32 { return attackedStack->count > 1 ? baseTextID + 1 : baseTextID; @@ -451,19 +429,19 @@ void DefaultSpellMechanics::battleLogSingleTarget(std::vector& logL } break; default: - { - MetaString line; - line.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s - //todo: use text 566 for single creature - parameters.caster->getCasterName(line); - line.addReplacement(MetaString::SPELL_NAME, owner->id.toEnum()); - displayDamage = true; - logLines.push_back(line); - } + displayDamage = true; + battleLogDefault(logLines, parameters, attacked); break; } } +void DefaultSpellMechanics::battleLogDefault(std::vector & logLines, const BattleSpellCastParameters & parameters, const std::vector & attacked) const +{ + MetaString line; + parameters.caster->getCastDescription(owner, attacked, line); + logLines.push_back(line); +} + void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { //applying effects diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 1103c531b..4f097bfca 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -59,8 +59,11 @@ public: void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const override final; - void battleLogSingleTarget(std::vector & logLines, const BattleSpellCastParameters & parameters, - const CStack * attackedStack, const si32 damageToDisplay, bool & displayDamage) const; + void battleLog(std::vector & logLines, const BattleSpellCastParameters & parameters, + const std::vector & attacked, const si32 damageToDisplay, bool & displayDamage) const; + + void battleLogDefault(std::vector & logLines, const BattleSpellCastParameters & parameters, + const std::vector & attacked) const; bool requiresCreatureTarget() const override; protected: diff --git a/lib/spells/Magic.h b/lib/spells/Magic.h index 0d93047db..32b43ad16 100644 --- a/lib/spells/Magic.h +++ b/lib/spells/Magic.h @@ -47,5 +47,9 @@ public: virtual const PlayerColor getOwner() const = 0; + ///only name substitution virtual void getCasterName(MetaString & text) const = 0; + + ///full default text + virtual void getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const = 0; }; From dcb0e8a0c15d5ef58c8bf6be7c21eeecc7e1b9c6 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 18 Sep 2016 13:39:02 +0300 Subject: [PATCH 47/60] Tweak --- server/CGameHandler.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4e9d0dd5b..9408a89a3 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5307,9 +5307,8 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat ) vstd::amin(chanceToKill, 1); //cap at 100% std::binomial_distribution<> distribution(attacker->count, chanceToKill); - std::mt19937 rng(std::time(nullptr)); - int staredCreatures = distribution(rng); + int staredCreatures = distribution(getRandomGenerator().getStdGenerator()); double cap = 1 / std::max(chanceToKill, (double)(0.01));//don't divide by 0 int maxToKill = (attacker->count + cap - 1) / cap; //not much more than chance * count From 1fd87ecc4a3afab14509ef755edee94af96ca444 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 18 Sep 2016 14:54:17 +0300 Subject: [PATCH 48/60] Simplified offensive spell damage calculation --- lib/spells/CDefaultSpellMechanics.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index a8fe43de0..6b95c5768 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -447,17 +447,12 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, //applying effects if(owner->isOffensiveSpell()) { - int spellDamage = parameters.effectValue; - + const int rawDamage = (parameters.effectValue == 0) ? owner->calculateRawEffectValue(parameters.effectLevel, parameters.effectPower) : parameters.effectValue; int chainLightningModifier = 0; for(auto & attackedCre : ctx.attackedCres) { BattleStackAttacked bsa; - if(spellDamage != 0) - bsa.damageAmount = owner->adjustRawDamage(parameters.caster, attackedCre, spellDamage) >> chainLightningModifier; - else - bsa.damageAmount = owner->calculateDamage(parameters.caster, attackedCre, parameters.effectLevel, parameters.effectPower) >> chainLightningModifier; - + bsa.damageAmount = owner->adjustRawDamage(parameters.caster, attackedCre, rawDamage) >> chainLightningModifier; ctx.addDamageToDisplay(bsa.damageAmount); bsa.stackAttacked = (attackedCre)->ID; From 73c7b49eb7e5857bae83a268553fe47173fb4ac9 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 18 Sep 2016 15:27:22 +0300 Subject: [PATCH 49/60] Hide effectValue calculation inside BattleSpellCastParameters --- lib/spells/BattleSpellMechanics.cpp | 8 ++------ lib/spells/CDefaultSpellMechanics.cpp | 2 +- lib/spells/ISpellMechanics.cpp | 5 +++++ lib/spells/ISpellMechanics.h | 4 ++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 1a535e4de..6a6a62172 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -41,9 +41,7 @@ void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, int HealingSpellMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, SpellCastContext& ctx) const { - if(parameters.effectValue != 0) - return parameters.effectValue; //Archangel - return owner->calculateRawEffectValue(parameters.effectLevel, parameters.effectPower); //??? + return parameters.getEffectValue(); } ///AntimagicMechanics @@ -712,7 +710,6 @@ void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, co int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, SpellCastContext& ctx) const { - int res = 0; const CStack * victim = nullptr; if(parameters.destinations.size() == 2) @@ -726,8 +723,7 @@ int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const return 0; } - res = (parameters.effectPower + victim->MaxHealth() + owner->getPower(parameters.effectLevel)) * victim->count; - return res; + return (parameters.effectPower + victim->MaxHealth() + owner->getPower(parameters.effectLevel)) * victim->count; } bool SacrificeMechanics::requiresCreatureTarget() const diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 6b95c5768..156fe55ef 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -447,7 +447,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, //applying effects if(owner->isOffensiveSpell()) { - const int rawDamage = (parameters.effectValue == 0) ? owner->calculateRawEffectValue(parameters.effectLevel, parameters.effectPower) : parameters.effectValue; + const int rawDamage = parameters.getEffectValue(); int chainLightningModifier = 0; for(auto & attackedCre : ctx.attackedCres) { diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index fabe2ea4e..163cedf91 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -88,6 +88,11 @@ BattleHex BattleSpellCastParameters::getFirstDestinationHex() const return destinations.at(0).hexValue; } +int BattleSpellCastParameters::getEffectValue() const +{ + return (effectValue == 0) ? spell->calculateRawEffectValue(effectLevel, effectPower) : effectValue; +} + ///ISpellMechanics ISpellMechanics::ISpellMechanics(CSpell * s): owner(s) diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 6636c836e..f7463344c 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -58,6 +58,8 @@ public: BattleHex getFirstDestinationHex() const; + int getEffectValue() const; + const CSpell * spell; const BattleInfo * cb; const ISpellCaster * caster; @@ -78,6 +80,8 @@ public: int effectPower; ///actual spell-power affecting effect duration int enchantPower; + +private: ///for Archangel-like casting int effectValue; }; From 374e30c972535e3a27587b22fec1bc484f9cac00 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 18 Sep 2016 16:30:03 +0300 Subject: [PATCH 50/60] Added a few safety checks --- lib/spells/BattleSpellMechanics.cpp | 41 ++++++++++++++++++++++++--- lib/spells/BattleSpellMechanics.h | 10 ++++--- lib/spells/CDefaultSpellMechanics.cpp | 2 +- lib/spells/CDefaultSpellMechanics.h | 2 +- lib/spells/CSpellHandler.cpp | 2 +- lib/spells/ISpellMechanics.h | 2 +- 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 6a6a62172..2a80762ce 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -341,8 +341,14 @@ void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, c env->sendAndApply(&ca); } -ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const +ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { + if(mode == ECastingMode::AFTER_ATTACK_CASTING || mode == ECastingMode::SPELL_LIKE_ATTACK || mode == ECastingMode::MAGIC_MIRROR) + { + logGlobal->warn("Invalid spell cast attempt: spell %s, mode %d", owner->name, mode); //should not even try to do it + return ESpellCastProblem::INVALID; + } + if(nullptr == cb->battleGetDefendedTown()) { return ESpellCastProblem::NO_APPROPRIATE_TARGET; @@ -592,8 +598,14 @@ void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * en env->complain("There's no obstacle to remove!"); } -ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const +ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { + if(mode == ECastingMode::AFTER_ATTACK_CASTING || mode == ECastingMode::SPELL_LIKE_ATTACK || mode == ECastingMode::MAGIC_MIRROR) + { + logGlobal->warn("Invalid spell cast attempt: spell %s, mode %d", owner->name, mode); //should not even try to do it + return ESpellCastProblem::INVALID; + } + const int spellLevel = caster->getSpellSchoolLevel(owner); for(auto obstacle : cb->battleGetAllObstacles()) @@ -653,8 +665,14 @@ HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectL } ///SacrificeMechanics -ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const +ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { + if(mode == ECastingMode::AFTER_ATTACK_CASTING || mode == ECastingMode::SPELL_LIKE_ATTACK || mode == ECastingMode::MAGIC_MIRROR) + { + logGlobal->warn("Invalid spell cast attempt: spell %s, mode %d", owner->name, mode); //should not even try to do it + return ESpellCastProblem::INVALID; + } + // for sacrifice we have to check for 2 targets (one dead to resurrect and one living to destroy) bool targetExists = false; @@ -767,8 +785,14 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac } ///SummonMechanics -ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const +ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { + if(mode == ECastingMode::AFTER_ATTACK_CASTING || mode == ECastingMode::SPELL_LIKE_ATTACK || mode == ECastingMode::MAGIC_MIRROR) + { + logGlobal->warn("Invalid spell cast attempt: spell %s, mode %d", owner->name, mode); //should not even try to do it + return ESpellCastProblem::INVALID; + } + //check if there are summoned elementals of other type auto otherSummoned = cb->battleGetStacksIf([caster, this](const CStack * st) @@ -848,4 +872,13 @@ void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, con } } +ESpellCastProblem::ESpellCastProblem TeleportMechanics::canBeCast(const CBattleInfoCallback* cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const +{ + if(mode == ECastingMode::AFTER_ATTACK_CASTING || mode == ECastingMode::SPELL_LIKE_ATTACK || mode == ECastingMode::MAGIC_MIRROR) + { + logGlobal->warn("Invalid spell cast attempt: spell %s, mode %d", owner->name, mode); //should not even try to do it + return ESpellCastProblem::INVALID; + } + return DefaultSpellMechanics::canBeCast(cb, mode, caster); +} diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 8c2064f42..acf12f3b6 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -85,7 +85,7 @@ class DLL_LINKAGE EarthquakeMechanics : public SpecialSpellMechanics { public: EarthquakeMechanics(CSpell * s): SpecialSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -165,7 +165,7 @@ class DLL_LINKAGE RemoveObstacleMechanics : public SpecialSpellMechanics { public: RemoveObstacleMechanics(CSpell * s): SpecialSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; bool requiresCreatureTarget() const override; protected: @@ -188,7 +188,7 @@ class DLL_LINKAGE SacrificeMechanics : public RisingSpellMechanics public: SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -209,7 +209,7 @@ class DLL_LINKAGE SummonMechanics : public SpecialSpellMechanics public: SummonMechanics(CSpell * s, CreatureID cre): SpecialSpellMechanics(s), creatureToSummon(cre){}; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -221,6 +221,8 @@ class DLL_LINKAGE TeleportMechanics: public DefaultSpellMechanics { public: TeleportMechanics(CSpell * s): DefaultSpellMechanics(s){}; + + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 156fe55ef..2d48b8cd3 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -700,7 +700,7 @@ std::vector DefaultSpellMechanics::calculateAffectedStacks(const return res; } -ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const +ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { //no problems by default, this method is for spell-specific problems return ESpellCastProblem::OK; diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 4f097bfca..38914ff17 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -50,7 +50,7 @@ public: std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const override final; - ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override; + ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 16c601c3e..c33d523fd 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -151,7 +151,7 @@ ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affecte ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { - const ESpellCastProblem::ESpellCastProblem generalProblem = mechanics->canBeCast(cb, caster); + const ESpellCastProblem::ESpellCastProblem generalProblem = mechanics->canBeCast(cb, mode, caster); if(generalProblem != ESpellCastProblem::OK) return generalProblem; diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index f7463344c..11aafe856 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -109,7 +109,7 @@ public: virtual std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0; virtual std::vector getAffectedStacks(const CBattleInfoCallback * cb, SpellTargetingContext & ctx) const = 0; - virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const = 0; + virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const = 0; virtual ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const = 0; From ef34d21941f3f31f0633a32479ffc9d775b8b377 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 18 Sep 2016 17:21:56 +0300 Subject: [PATCH 51/60] Passive cast can be blocked by spell level limit. --- lib/CBattleCallback.cpp | 4 +++- server/CGameHandler.cpp | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index cebb8c5f5..64bd8422e 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1666,7 +1666,9 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell if(specificProblem != ESpellCastProblem::OK) return specificProblem; - if(battleMaxSpellLevel(side) < spell->level) //effect like Recanter's Cloak or Orb of Inhibition + //effect like Recanter's Cloak. Blocks also passive casting. + //TODO: check creature abilities to block + if(battleMaxSpellLevel(side) < spell->level) return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED; return ESpellCastProblem::OK; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9408a89a3..e12112dd3 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5578,13 +5578,17 @@ void CGameHandler::runBattle() for(int i = 0; i < 2; ++i) { auto h = gs->curB->battleGetFightingHero(i); - if(h && h->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) + if(h) { TBonusListPtr bl = h->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL)); for (Bonus *b : *bl) { const CSpell * spell = SpellID(b->subtype).toSpell(); + + if(ESpellCastProblem::OK != gs->curB->battleCanCastThisSpell(h, spell, ECastingMode::PASSIVE_CASTING)) + continue; + BattleSpellCastParameters parameters(gs->curB, h, spell); parameters.spellLvl = 3; parameters.effectLevel = 3; From 7e85154946c0232870a386eaf54f3f6322e84ade Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 18 Sep 2016 18:12:07 +0300 Subject: [PATCH 52/60] Unify battleCanCastSpell parameters --- lib/CBattleCallback.cpp | 28 ++++++++++++++++++++++------ lib/CBattleCallback.h | 3 +-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 64bd8422e..88e50284d 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -345,9 +345,15 @@ const IBonusBearer * CBattleInfoEssentials::getBattleNode() const return getBattle(); } -ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(PlayerColor player, ECastingMode::ECastingMode mode) const +ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const { RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID); + if(caster == nullptr) + { + logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastSpell: no spellcaster."; + return ESpellCastProblem::INVALID; + } + const PlayerColor player = caster->getOwner(); const ui8 side = playerToSide(player); if(!battleDoWeKnowAbout(side)) { @@ -355,16 +361,17 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(Pla return ESpellCastProblem::INVALID; } + if(battleTacticDist()) + return ESpellCastProblem::ONGOING_TACTIC_PHASE; + switch (mode) { case ECastingMode::HERO_CASTING: { - if(battleTacticDist()) - return ESpellCastProblem::ONGOING_TACTIC_PHASE; if(battleCastSpells(side) > 0) return ESpellCastProblem::ALREADY_CASTED_THIS_TURN; - auto hero = battleGetFightingHero(side); + auto hero = dynamic_cast(caster); if(!hero) return ESpellCastProblem::NO_HERO_TO_CAST_SPELL; @@ -1640,7 +1647,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell if(!battleDoWeKnowAbout(side)) return ESpellCastProblem::INVALID; - ESpellCastProblem::ESpellCastProblem genProblem = battleCanCastSpell(player, mode); + ESpellCastProblem::ESpellCastProblem genProblem = battleCanCastSpell(caster, mode); if(genProblem != ESpellCastProblem::OK) return genProblem; @@ -2079,7 +2086,16 @@ bool CPlayerBattleCallback::battleCanCastSpell(ESpellCastProblem::ESpellCastProb { RETURN_IF_NOT_BATTLE(false); ASSERT_IF_CALLED_WITH_PLAYER - auto problem = CBattleInfoCallback::battleCanCastSpell(*player, ECastingMode::HERO_CASTING); + + const CGHeroInstance * hero = battleGetMyHero(); + if(!hero) + { + if(outProblem) + *outProblem = ESpellCastProblem::NO_HERO_TO_CAST_SPELL; + return false; + } + + auto problem = CBattleInfoCallback::battleCanCastSpell(hero, ECastingMode::HERO_CASTING); if(outProblem) *outProblem = problem; diff --git a/lib/CBattleCallback.h b/lib/CBattleCallback.h index 6fc763f8c..ca2677ed7 100644 --- a/lib/CBattleCallback.h +++ b/lib/CBattleCallback.h @@ -212,7 +212,6 @@ public: TStacks battleAliveStacks(ui8 side) const; const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const; - //ESpellCastProblem::ESpellCastProblem battleCanCastSpell(int player, ECastingMode::ECastingMode mode) const; //Checks if player is able to cast spells (at all) at the moment }; struct DLL_LINKAGE BattleAttackInfo @@ -282,7 +281,7 @@ public: //*** MAGIC si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell - ESpellCastProblem::ESpellCastProblem battleCanCastSpell(PlayerColor player, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell + ESpellCastProblem::ESpellCastProblem battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode) const; //checks if given player can cast given spell ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode From 21de5817e347042c8347c83209ef8c695ad7f130 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 22 Sep 2016 18:44:57 +0300 Subject: [PATCH 53/60] fixed http://bugs.vcmi.eu/view.php?id=2499 --- client/battle/CBattleInterface.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index c9e4b5ace..d438db32d 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -3010,18 +3010,27 @@ void CBattleInterface::showHighlightedHexes(SDL_Surface * to) } if (settings["battle"]["mouseShadow"].Bool()) { - if(spellToCast) //when casting spell + const ISpellCaster * caster = nullptr; + const CSpell * spell = nullptr; + + if(spellToCast)//hero casts spell + { + spell = SpellID(spellToCast->additionalInfo).toSpell(); + caster = activeStack->attackerOwned ? attackingHeroInstance : defendingHeroInstance; + } + else if(creatureSpellToCast >= 0 && stackCanCastSpell && creatureCasting)//stack casts spell + { + spell = SpellID(creatureSpellToCast).toSpell(); + caster = activeStack; + } + + if(caster && spell) //when casting spell { //calculating spell school level - const CSpell & spToCast = *CGI->spellh->objects[spellToCast->additionalInfo]; - ui8 schoolLevel = 0; - - auto caster = activeStack->attackerOwned ? attackingHeroInstance : defendingHeroInstance; - if (caster) - schoolLevel = caster->getSpellSchoolLevel(&spToCast); + ui8 schoolLevel = caster->getSpellSchoolLevel(spell); // printing shaded hex(es) - auto shaded = spToCast.rangeInHexes(currentlyHoveredHex, schoolLevel, curInt->cb->battleGetMySide()); + auto shaded = spell->rangeInHexes(currentlyHoveredHex, schoolLevel, curInt->cb->battleGetMySide()); for(BattleHex shadedHex : shaded) { if ((shadedHex.getX() != 0) && (shadedHex.getX() != GameConstants::BFIELD_WIDTH -1)) From fb384d83b837ffbb9894f1ecc923e00c43813a5a Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 22 Sep 2016 20:51:13 +0300 Subject: [PATCH 54/60] Probably fixed crash when AI finish battle with spell --- AI/BattleAI/BattleAI.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 7c3c5aefe..0967b7228 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -103,11 +103,6 @@ void CBattleAI::init(std::shared_ptr CB) CB->unlockGsWhenWaiting = false; } -static bool thereRemainsEnemy() -{ - return !cbc->battleIsFinished(); -} - BattleAction CBattleAI::activeStack( const CStack * stack ) { LOG_TRACE_PARAMS(logAi, "stack: %s", stack->nodeName()) ; @@ -136,21 +131,20 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) if(cb->battleCanCastSpell()) attemptCastingSpell(); - if(!thereRemainsEnemy()) - return BattleAction(); + if(auto ret = cbc->battleIsFinished()) + { + //spellcast may finish battle + //send special preudo-action + BattleAction cancel; + cancel.actionType = Battle::CANCEL; + return cancel; + } if(auto action = considerFleeingOrSurrendering()) return *action; - if(cb->battleGetStacks(CBattleInfoEssentials::ONLY_ENEMY).empty()) - { - //We apparently won battle by casting spell, return defend... (accessing cb may cause trouble) - return BattleAction::makeDefend(stack); - } - PotentialTargets targets(stack); - if(targets.possibleAttacks.size()) { auto hlp = targets.bestAction(); From 62d272f3e536d188159498e445730bacbc39be8a Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 22 Sep 2016 21:35:37 +0300 Subject: [PATCH 55/60] Cleanup --- server/CGameHandler.cpp | 2 +- server/CQuery.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index cc94c8f16..18f8089f6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2520,7 +2520,7 @@ void CGameHandler::heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) void CGameHandler::sendToAllClients( CPackForClient * info ) { - logGlobal->trace("Sending to all clients a package of type %s", typeid(*info).name()); + logNetwork->trace("Sending to all clients a package of type %s", typeid(*info).name()); for(auto & elem : conns) { boost::unique_lock lock(*(elem)->wmx); diff --git a/server/CQuery.cpp b/server/CQuery.cpp index 886b478f4..2def7689e 100644 --- a/server/CQuery.cpp +++ b/server/CQuery.cpp @@ -126,7 +126,7 @@ void CObjectVisitQuery::onExposure(CGameHandler *gh, QueryPtr topQuery) void Queries::popQuery(PlayerColor player, QueryPtr query) { - LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query); + //LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query); if(topQuery(player) != query) { logGlobal->trace("Cannot remove, not a top!"); @@ -147,7 +147,7 @@ void Queries::popQuery(PlayerColor player, QueryPtr query) void Queries::popQuery(const CQuery &query) { - LOG_TRACE_PARAMS(logGlobal, "query='%s'", query); + //LOG_TRACE_PARAMS(logGlobal, "query='%s'", query); assert(query.players.size()); for(auto player : query.players) @@ -174,7 +174,7 @@ void Queries::addQuery(QueryPtr query) void Queries::addQuery(PlayerColor player, QueryPtr query) { - LOG_TRACE_PARAMS(logGlobal, "player='%d', query='%s'", player.getNum() % query); + //LOG_TRACE_PARAMS(logGlobal, "player='%d', query='%s'", player.getNum() % query); query->onAdding(gh, player); queries[player].push_back(query); } @@ -186,7 +186,7 @@ QueryPtr Queries::topQuery(PlayerColor player) void Queries::popIfTop(QueryPtr query) { - LOG_TRACE_PARAMS(logGlobal, "query='%d'", query); + //LOG_TRACE_PARAMS(logGlobal, "query='%d'", query); if(!query) logGlobal->error("The query is nullptr! Ignoring."); From bd79298ca6a7604904fc8225a50e2439fedf03a9 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 22 Sep 2016 22:29:48 +0300 Subject: [PATCH 56/60] Let AI do not self-destruct with armageddon too often. --- AI/BattleAI/BattleAI.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 0967b7228..3ad085261 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -352,6 +352,7 @@ struct PossibleSpellcast { const CSpell *spell; BattleHex dest; + si32 value; }; struct CurrentOffensivePotential @@ -426,7 +427,7 @@ void CBattleAI::attemptCastingSpell() { for(auto hex : getTargetsToConsider(spell, hero)) { - PossibleSpellcast ps = {spell, hex}; + PossibleSpellcast ps = {spell, hex, 0}; possibleCasts.push_back(ps); } } @@ -466,11 +467,10 @@ void CBattleAI::attemptCastingSpell() damageDealt += dmg; } - const int damageDiff = damageDealt - damageReceived; + const int damageDiff = damageDealt - damageReceived * 10; - - LOGFL("Casting %s on hex %d would deal %d damage points among %d stacks.", - ps.spell->name % ps.dest % damageDiff % stacksSuffering.size()); + LOGFL("Casting %s on hex %d would deal { %d %d } damage points among %d stacks.", + ps.spell->name % ps.dest % damageDealt % damageReceived % stacksSuffering.size()); //TODO tactic effect too return damageDiff; } @@ -520,7 +520,15 @@ void CBattleAI::attemptCastingSpell() } }; - auto castToPerform = *vstd::maxElementByFun(possibleCasts, evaluateSpellcast); + for(PossibleSpellcast & psc : possibleCasts) + psc.value = evaluateSpellcast(psc); + + auto pscValue = [] (const PossibleSpellcast &ps) -> int + { + return ps.value; + }; + + auto castToPerform = *vstd::maxElementByFun(possibleCasts, pscValue); LOGFL("Best spell is %s. Will cast.", castToPerform.spell->name); BattleAction spellcast; From 44753866eca225ab990b858be453d3edf6f1531c Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Fri, 23 Sep 2016 12:20:41 +0300 Subject: [PATCH 57/60] Fixed Clone links cleanup. --- lib/NetPacksLib.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index fc301ba7b..3f21ffa68 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1712,6 +1712,13 @@ DLL_LINKAGE void BattleStacksRemoved::applyGs( CGameState *gs ) toRemove->cloneID = -1; } + //cleanup remaining clone links if any + for(CStack * s : gs->curB->stacks) + { + if(s->cloneID == toRemove->ID) + s->cloneID = -1; + } + break; } } From bac0b026e566334c6a3f5a4f1e716a0521319519 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Fri, 23 Sep 2016 13:14:12 +0300 Subject: [PATCH 58/60] Do not place obstacles on wall parts --- lib/spells/BattleSpellMechanics.cpp | 31 +++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 2a80762ce..72832370e 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -399,28 +399,39 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I ///ObstacleMechanics ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { + ui8 side = cb->playerToSide(ctx.caster->getOwner()); + + bool hexesOutsideBattlefield = false; + + auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side, &hexesOutsideBattlefield); + if(ctx.ti.clearAffected) { - ui8 side = cb->playerToSide(ctx.caster->getOwner()); - - bool hexesOutsideBattlefield = false; - - auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side, &hexesOutsideBattlefield); - for(BattleHex hex : tilesThatMustBeClear) { if(cb->battleGetStackByPos(hex, true) || !!cb->battleGetObstacleOnPos(hex, false) || !hex.isAvailable()) { return ESpellCastProblem::NO_APPROPRIATE_TARGET; } - } - if(hexesOutsideBattlefield) - { - return ESpellCastProblem::NO_APPROPRIATE_TARGET; + if(nullptr != cb->battleGetDefendedTown() && CGTownInstance::NONE != cb->battleGetDefendedTown()->fortLevel()) + { + EWallPart::EWallPart part = cb->battleHexToWallPart(hex); + + if(part != EWallPart::INVALID) + { + if(cb->battleGetWallState(part) != EWallState::DESTROYED && cb->battleGetWallState(part) != EWallState::NONE) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + } } } + if(hexesOutsideBattlefield) + { + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + return ESpellCastProblem::OK; } From cffc4b2ab50602d9c7387b9acf109f7dfea66cc1 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Fri, 23 Sep 2016 20:27:55 +0300 Subject: [PATCH 59/60] fixed patch obstacle placement inside walls --- lib/spells/BattleSpellMechanics.cpp | 54 +++++++++++++++++------------ lib/spells/BattleSpellMechanics.h | 1 + 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 72832370e..3cac1b245 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -405,34 +405,42 @@ ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleI auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side, &hexesOutsideBattlefield); - if(ctx.ti.clearAffected) + for(const BattleHex & hex : tilesThatMustBeClear) + if(!isHexAviable(cb, hex, ctx.ti.clearAffected)) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + + if(hexesOutsideBattlefield) + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + + return ESpellCastProblem::OK; +} + +bool ObstacleMechanics::isHexAviable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear) +{ + if(!hex.isAvailable()) + return false; + + if(!mustBeClear) + return true; + + if(cb->battleGetStackByPos(hex, true) || !!cb->battleGetObstacleOnPos(hex, false)) + return false; + + if(nullptr != cb->battleGetDefendedTown() && CGTownInstance::NONE != cb->battleGetDefendedTown()->fortLevel()) { - for(BattleHex hex : tilesThatMustBeClear) + EWallPart::EWallPart part = cb->battleHexToWallPart(hex); + + if(part != EWallPart::INVALID) { - if(cb->battleGetStackByPos(hex, true) || !!cb->battleGetObstacleOnPos(hex, false) || !hex.isAvailable()) - { - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } + if(static_cast(part) < 0) + return false;//indestuctible part, cant be checked by battleGetWallState - if(nullptr != cb->battleGetDefendedTown() && CGTownInstance::NONE != cb->battleGetDefendedTown()->fortLevel()) - { - EWallPart::EWallPart part = cb->battleHexToWallPart(hex); - - if(part != EWallPart::INVALID) - { - if(cb->battleGetWallState(part) != EWallState::DESTROYED && cb->battleGetWallState(part) != EWallState::NONE) - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } - } + if(cb->battleGetWallState(part) != EWallState::DESTROYED && cb->battleGetWallState(part) != EWallState::NONE) + return false; } } - if(hexesOutsideBattlefield) - { - return ESpellCastProblem::NO_APPROPRIATE_TARGET; - } - - return ESpellCastProblem::OK; + return true; } void ObstacleMechanics::placeObstacle(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const BattleHex & pos) const @@ -461,7 +469,7 @@ void PatchObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1) { BattleHex hex = i; - if(hex.getX() > 0 && hex.getX() < 16 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) + if(isHexAviable(parameters.cb, hex, true)) availableTiles.push_back(hex); } RandomGeneratorUtil::randomShuffle(availableTiles, env->getRandomGenerator()); diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index acf12f3b6..62ffe596f 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -104,6 +104,7 @@ public: ObstacleMechanics(CSpell * s): SpecialSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; protected: + static bool isHexAviable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear); void placeObstacle(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const BattleHex & pos) const; virtual void setupObstacle(SpellCreatedObstacle * obstacle) const = 0; }; From 7618e294c270fd4013ccf0ad7c2fff92cb6c6a59 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 24 Sep 2016 04:55:48 +0300 Subject: [PATCH 60/60] More fixes to not place obstacles on wall parts --- lib/CBattleCallback.cpp | 3 ++- lib/spells/BattleSpellMechanics.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index b2a915a4c..8ddc1d3ed 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -70,7 +70,8 @@ namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO std::make_pair(45, EWallPart::INDESTRUCTIBLE_PART), std::make_pair(62, EWallPart::INDESTRUCTIBLE_PART), std::make_pair(112, EWallPart::INDESTRUCTIBLE_PART), - std::make_pair(147, EWallPart::INDESTRUCTIBLE_PART) + std::make_pair(147, EWallPart::INDESTRUCTIBLE_PART), + std::make_pair(165, EWallPart::INDESTRUCTIBLE_PART) }; static EWallPart::EWallPart hexToWallPart(BattleHex hex) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 3cac1b245..fc0618476 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -430,14 +430,14 @@ bool ObstacleMechanics::isHexAviable(const CBattleInfoCallback * cb, const Battl { EWallPart::EWallPart part = cb->battleHexToWallPart(hex); - if(part != EWallPart::INVALID) - { - if(static_cast(part) < 0) - return false;//indestuctible part, cant be checked by battleGetWallState - - if(cb->battleGetWallState(part) != EWallState::DESTROYED && cb->battleGetWallState(part) != EWallState::NONE) - return false; - } + if(part == EWallPart::INVALID) + return true;//no fortification here + else if(static_cast(part) < 0) + return false;//indestuctible part (cant be checked by battleGetWallState) + else if(part == EWallPart::BOTTOM_TOWER || part == EWallPart::UPPER_TOWER) + return false;//destructible, but should not be available + else if(cb->battleGetWallState(part) != EWallState::DESTROYED && cb->battleGetWallState(part) != EWallState::NONE) + return false; } return true;