|
@@ -13,6 +13,37 @@
|
|
|
|
|
|
#include "../NetPacks.h"
|
|
|
#include "../BattleState.h"
|
|
|
+#include "../mapObjects/CGHeroInstance.h"
|
|
|
+
|
|
|
+///HealingSpellMechanics
|
|
|
+void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+{
|
|
|
+ EHealLevel healLevel = getHealLevel(parameters.effectLevel);
|
|
|
+ int hpGained = calculateHealedHP(env, parameters, ctx);
|
|
|
+ StacksHealedOrResurrected shr;
|
|
|
+ shr.lifeDrain = false;
|
|
|
+ shr.tentHealing = false;
|
|
|
+
|
|
|
+ const bool resurrect = (healLevel != EHealLevel::HEAL);
|
|
|
+ for(auto & attackedCre : ctx.attackedCres)
|
|
|
+ {
|
|
|
+ StacksHealedOrResurrected::HealInfo hi;
|
|
|
+ hi.stackID = (attackedCre)->ID;
|
|
|
+ int stackHPgained = parameters.caster->getSpellBonus(owner, hpGained, attackedCre);
|
|
|
+ hi.healedHP = attackedCre->calculateHealedHealthPoints(stackHPgained, resurrect);
|
|
|
+ hi.lowLevelResurrection = (healLevel == EHealLevel::RESURRECT);
|
|
|
+ shr.healedStacks.push_back(hi);
|
|
|
+ }
|
|
|
+ if(!shr.healedStacks.empty())
|
|
|
+ env->sendAndApply(&shr);
|
|
|
+}
|
|
|
+
|
|
|
+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); //???
|
|
|
+}
|
|
|
|
|
|
///AntimagicMechanics
|
|
|
void AntimagicMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
|
@@ -67,7 +98,7 @@ std::set<const CStack *> ChainLightningMechanics::getAffectedStacks(SpellTargeti
|
|
|
}
|
|
|
|
|
|
///CloneMechanics
|
|
|
-void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
const CStack * clonedStack = nullptr;
|
|
|
if(ctx.attackedCres.size())
|
|
@@ -101,23 +132,21 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battle
|
|
|
env->sendAndApply(&ssp);
|
|
|
}
|
|
|
|
|
|
-ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
|
|
+ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
|
|
|
{
|
|
|
//can't clone already cloned creature
|
|
|
if(vstd::contains(obj->state, EBattleStackState::CLONED))
|
|
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
|
if(obj->cloneID != -1)
|
|
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
|
- //TODO: how about stacks casting Clone?
|
|
|
- //currently Clone casted by stack is assumed Expert level
|
|
|
ui8 schoolLevel;
|
|
|
if(caster)
|
|
|
{
|
|
|
- schoolLevel = caster->getSpellSchoolLevel(owner);
|
|
|
+ schoolLevel = caster->getEffectLevel(owner);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- schoolLevel = 3;
|
|
|
+ schoolLevel = 3;//todo: remove
|
|
|
}
|
|
|
|
|
|
if(schoolLevel < 3)
|
|
@@ -146,6 +175,11 @@ void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * pac
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+HealingSpellMechanics::EHealLevel CureMechanics::getHealLevel(int effectLevel) const
|
|
|
+{
|
|
|
+ return EHealLevel::HEAL;
|
|
|
+}
|
|
|
+
|
|
|
///DispellMechanics
|
|
|
void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
|
|
{
|
|
@@ -153,21 +187,35 @@ void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast *
|
|
|
doDispell(battle, packet, Selector::sourceType(Bonus::SPELL_EFFECT));
|
|
|
}
|
|
|
|
|
|
-ESpellCastProblem::ESpellCastProblem DispellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
|
|
+ESpellCastProblem::ESpellCastProblem DispellMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
|
|
|
{
|
|
|
- //DISPELL ignores all immunities, so do not call default
|
|
|
- std::stringstream cachingStr;
|
|
|
- cachingStr << "source_" << Bonus::SPELL_EFFECT;
|
|
|
-
|
|
|
- if(obj->hasBonus(Selector::sourceType(Bonus::SPELL_EFFECT), cachingStr.str()))
|
|
|
{
|
|
|
- return ESpellCastProblem::OK;
|
|
|
+ //just in case
|
|
|
+ if(!obj->alive())
|
|
|
+ return ESpellCastProblem::WRONG_SPELL_TARGET;
|
|
|
}
|
|
|
+ //DISPELL ignores 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;
|
|
|
+ }
|
|
|
+ {
|
|
|
+ std::stringstream cachingStr;
|
|
|
+ cachingStr << "source_" << Bonus::SPELL_EFFECT;
|
|
|
|
|
|
+ if(obj->hasBonus(Selector::sourceType(Bonus::SPELL_EFFECT), cachingStr.str()))
|
|
|
+ {
|
|
|
+ return ESpellCastProblem::OK;
|
|
|
+ }
|
|
|
+ }
|
|
|
return ESpellCastProblem::WRONG_SPELL_TARGET;
|
|
|
+ //any other immunities are ignored - do not execute default algorithm
|
|
|
}
|
|
|
|
|
|
-void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
DefaultSpellMechanics::applyBattleEffects(env, parameters, ctx);
|
|
|
|
|
@@ -190,15 +238,15 @@ void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, Batt
|
|
|
}
|
|
|
|
|
|
///EarthquakeMechanics
|
|
|
-void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
- if(nullptr == parameters.cb->town)
|
|
|
+ if(nullptr == parameters.cb->battleGetDefendedTown())
|
|
|
{
|
|
|
env->complain("EarthquakeMechanics: not town siege");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if(CGTownInstance::NONE == parameters.cb->town->fortLevel())
|
|
|
+ if(CGTownInstance::NONE == parameters.cb->battleGetDefendedTown()->fortLevel())
|
|
|
{
|
|
|
env->complain("EarthquakeMechanics: town has no fort");
|
|
|
return;
|
|
@@ -277,7 +325,7 @@ void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, B
|
|
|
env->sendAndApply(&ca);
|
|
|
}
|
|
|
|
|
|
-ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
|
|
|
+ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
|
|
|
{
|
|
|
if(nullptr == cb->battleGetDefendedTown())
|
|
|
{
|
|
@@ -288,8 +336,9 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBat
|
|
|
{
|
|
|
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
|
|
}
|
|
|
-
|
|
|
- if(owner->getTargetInfo(0).smart) //TODO: use real spell level
|
|
|
+
|
|
|
+ CSpell::TargetInfo ti(owner, 0);//TODO: use real spell level
|
|
|
+ if(ti.smart)
|
|
|
{
|
|
|
//if spell targeting is smart, then only attacker can use it
|
|
|
if(cb->playerToSide(player) != 0)
|
|
@@ -300,15 +349,15 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBat
|
|
|
}
|
|
|
|
|
|
///HypnotizeMechanics
|
|
|
-ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
|
|
+ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
|
|
|
{
|
|
|
- if(nullptr != caster) //do not resist hypnotize casted after attack, for example
|
|
|
+ //todo: maybe do not resist on passive cast
|
|
|
+ if(nullptr != caster)
|
|
|
{
|
|
|
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
|
|
|
ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
|
|
|
//apply 'damage' bonus for hypnotize, including hero specialty
|
|
|
- ui64 maxHealth = caster->getSpellBonus(owner, caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
|
|
|
- * owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), obj);
|
|
|
+ ui64 maxHealth = caster->getSpellBonus(owner, owner->calculateRawEffectValue(caster->getEffectLevel(owner), caster->getEffectPower(owner)), obj);
|
|
|
if (subjectHealth > maxHealth)
|
|
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
|
}
|
|
@@ -316,9 +365,9 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C
|
|
|
}
|
|
|
|
|
|
///ObstacleMechanics
|
|
|
-void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
- auto placeObstacle = [&, this](BattleHex pos)
|
|
|
+ auto placeObstacle = [&, this](const BattleHex & pos)
|
|
|
{
|
|
|
static int obstacleIdToGive = parameters.cb->obstacles.size()
|
|
|
? (parameters.cb->obstacles.back()->uniqueID+1)
|
|
@@ -355,8 +404,8 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat
|
|
|
obstacle->pos = pos;
|
|
|
obstacle->casterSide = parameters.casterSide;
|
|
|
obstacle->ID = owner->id;
|
|
|
- obstacle->spellLevel = parameters.spellLvl;
|
|
|
- obstacle->casterSpellPower = parameters.usedSpellPower;
|
|
|
+ obstacle->spellLevel = parameters.effectLevel;
|
|
|
+ obstacle->casterSpellPower = parameters.effectPower;
|
|
|
obstacle->uniqueID = obstacleIdToGive++;
|
|
|
|
|
|
BattleObstaclePlaced bop;
|
|
@@ -364,6 +413,8 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat
|
|
|
env->sendAndApply(&bop);
|
|
|
};
|
|
|
|
|
|
+ const BattleHex destination = parameters.getFirstDestinationHex();
|
|
|
+
|
|
|
switch(owner->id)
|
|
|
{
|
|
|
case SpellID::QUICKSAND:
|
|
@@ -388,12 +439,22 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat
|
|
|
|
|
|
break;
|
|
|
case SpellID::FORCE_FIELD:
|
|
|
- placeObstacle(parameters.destination);
|
|
|
+ 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(parameters.destination, parameters.spellLvl, parameters.casterSide);
|
|
|
+ auto affectedHexes = owner->rangeInHexes(destination, parameters.spellLvl, parameters.casterSide);
|
|
|
for(BattleHex hex : affectedHexes)
|
|
|
placeObstacle(hex);
|
|
|
}
|
|
@@ -442,9 +503,9 @@ std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch
|
|
|
}
|
|
|
|
|
|
///RemoveObstacleMechanics
|
|
|
-void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
- if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.destination, false))
|
|
|
+ if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.getFirstDestinationHex(), false))
|
|
|
{
|
|
|
ObstaclesRemoved obr;
|
|
|
obr.obstacles.insert(obstacleToRemove->uniqueID);
|
|
@@ -454,20 +515,34 @@ void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * en
|
|
|
env->complain("There's no obstacle to remove!");
|
|
|
}
|
|
|
|
|
|
-///SpecialRisingSpellMechanics
|
|
|
-ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
|
|
|
+HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const
|
|
|
+{
|
|
|
+ //this may be even distinct class
|
|
|
+ if((effectLevel <= 1) && (owner->id == SpellID::RESURRECTION))
|
|
|
+ return EHealLevel::RESURRECT;
|
|
|
+
|
|
|
+ return EHealLevel::TRUE_RESURRECT;
|
|
|
+}
|
|
|
+
|
|
|
+///SacrificeMechanics
|
|
|
+ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) 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
|
|
|
//therefore we do not need to check caster and casting mode
|
|
|
//TODO: check that we really should check immunity for both stacks
|
|
|
- ESpellCastProblem::ESpellCastProblem res = owner->isImmuneBy(stack);
|
|
|
+ ESpellCastProblem::ESpellCastProblem res = owner->internalIsImmune(caster, stack);
|
|
|
const bool immune = ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res;
|
|
|
const bool casterStack = stack->owner == player;
|
|
|
|
|
@@ -489,39 +564,57 @@ ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCasted(const CBatt
|
|
|
}
|
|
|
|
|
|
|
|
|
-void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
- RisingSpellMechanics::applyBattleEffects(env, parameters, ctx);
|
|
|
-
|
|
|
- if(parameters.selectedStack == parameters.cb->battleActiveStack())
|
|
|
- //set another active stack than the one removed, or bad things will happen
|
|
|
- //TODO: make that part of BattleStacksRemoved? what about client update?
|
|
|
+ const CStack * victim = nullptr;
|
|
|
+ if(parameters.destinations.size() == 2)
|
|
|
{
|
|
|
- //makeStackDoNothing(gs->curB->getStack (selectedStack));
|
|
|
-
|
|
|
- BattleSetActiveStack sas;
|
|
|
-
|
|
|
- //std::vector<const CStack *> hlp;
|
|
|
- //battleGetStackQueue(hlp, 1, selectedStack); //next after this one
|
|
|
-
|
|
|
- //if(hlp.size())
|
|
|
- //{
|
|
|
- // sas.stack = hlp[0]->ID;
|
|
|
- //}
|
|
|
- //else
|
|
|
- // complain ("No new stack to activate!");
|
|
|
- sas.stack = parameters.cb->getNextStack()->ID; //why the hell next stack has same ID as current?
|
|
|
- env->sendAndApply(&sas);
|
|
|
-
|
|
|
+ victim = parameters.destinations[1].stackValue;
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //todo: remove and report error
|
|
|
+ victim = parameters.selectedStack;
|
|
|
+ }
|
|
|
+ if(nullptr == victim)
|
|
|
+ {
|
|
|
+ env->complain("SacrificeMechanics: No stack to sacrifice");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //resurrect target after basic checks
|
|
|
+ RisingSpellMechanics::applyBattleEffects(env, parameters, ctx);
|
|
|
+ //it is safe to remove even active stack
|
|
|
BattleStacksRemoved bsr;
|
|
|
- bsr.stackIDs.insert(parameters.selectedStack->ID); //somehow it works for teleport?
|
|
|
+ bsr.stackIDs.insert(victim->ID);
|
|
|
env->sendAndApply(&bsr);
|
|
|
}
|
|
|
|
|
|
+int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, SpellCastContext& ctx) const
|
|
|
+{
|
|
|
+ int res = 0;
|
|
|
+ const CStack * victim = nullptr;
|
|
|
+
|
|
|
+ if(parameters.destinations.size() == 2)
|
|
|
+ {
|
|
|
+ victim = parameters.destinations[1].stackValue;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //todo: remove and report error
|
|
|
+ victim = parameters.selectedStack;
|
|
|
+ }
|
|
|
+ if(nullptr == victim)
|
|
|
+ {
|
|
|
+ env->complain("SacrificeMechanics: No stack to sacrifice");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ res = (parameters.effectPower + victim->MaxHealth() + owner->getPower(parameters.effectLevel)) * victim->count;
|
|
|
+ return res;
|
|
|
+}
|
|
|
|
|
|
///SpecialRisingSpellMechanics
|
|
|
-ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
|
|
+ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
|
|
|
{
|
|
|
// following does apply to resurrect and animate dead(?) only
|
|
|
// for sacrifice health calculation and health limit check don't matter
|
|
@@ -529,18 +622,19 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac
|
|
|
if(obj->count >= obj->baseAmount)
|
|
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
|
|
|
|
- if(caster) //FIXME: Archangels can cast immune stack
|
|
|
- {
|
|
|
- auto maxHealth = calculateHealedHP(caster, obj, nullptr);
|
|
|
- if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature
|
|
|
- return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
|
- }
|
|
|
+ //FIXME: Archangels can cast immune stack and this should be applied for them and not hero
|
|
|
+// if(caster)
|
|
|
+// {
|
|
|
+// auto maxHealth = calculateHealedHP(caster, obj, nullptr);
|
|
|
+// if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature
|
|
|
+// return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
|
+// }
|
|
|
|
|
|
return DefaultSpellMechanics::isImmuneByStack(caster,obj);
|
|
|
}
|
|
|
|
|
|
///SummonMechanics
|
|
|
-ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
|
|
|
+ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
|
|
|
{
|
|
|
const ui8 side = cb->playerToSide(player);
|
|
|
|
|
@@ -559,7 +653,7 @@ ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCasted(const CBattleI
|
|
|
return ESpellCastProblem::OK;
|
|
|
}
|
|
|
|
|
|
-void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
BattleStackAdded bsa;
|
|
|
bsa.creID = creatureToSummon;
|
|
@@ -570,7 +664,7 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battl
|
|
|
//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
|
|
|
int percentBonus = parameters.casterHero ? parameters.casterHero->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0;
|
|
|
|
|
|
- bsa.amount = parameters.usedSpellPower
|
|
|
+ bsa.amount = parameters.effectPower
|
|
|
* owner->getPower(parameters.spellLvl)
|
|
|
* (100 + percentBonus) / 100.0; //new feature - percentage bonus
|
|
|
if(bsa.amount)
|
|
@@ -580,15 +674,46 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battl
|
|
|
}
|
|
|
|
|
|
///TeleportMechanics
|
|
|
-void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
+void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
|
|
{
|
|
|
- BattleStackMoved bsm;
|
|
|
- bsm.distance = -1;
|
|
|
- bsm.stack = parameters.selectedStack->ID;
|
|
|
- std::vector<BattleHex> tiles;
|
|
|
- tiles.push_back(parameters.destination);
|
|
|
- bsm.tilesToMove = tiles;
|
|
|
- bsm.teleporting = true;
|
|
|
- env->sendAndApply(&bsm);
|
|
|
+ //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;
|
|
|
+ if(!destination.isValid())
|
|
|
+ {
|
|
|
+ env->complain("TeleportMechanics: invalid teleport destination");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ BattleStackMoved bsm;
|
|
|
+ bsm.distance = -1;
|
|
|
+ bsm.stack = target->ID;
|
|
|
+ std::vector<BattleHex> tiles;
|
|
|
+ tiles.push_back(destination);
|
|
|
+ bsm.tilesToMove = tiles;
|
|
|
+ bsm.teleporting = true;
|
|
|
+ env->sendAndApply(&bsm);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //todo: remove and report error
|
|
|
+ BattleStackMoved bsm;
|
|
|
+ bsm.distance = -1;
|
|
|
+ bsm.stack = parameters.selectedStack->ID;
|
|
|
+ std::vector<BattleHex> tiles;
|
|
|
+ tiles.push_back(parameters.getFirstDestinationHex());
|
|
|
+ bsm.tilesToMove = tiles;
|
|
|
+ bsm.teleporting = true;
|
|
|
+ env->sendAndApply(&bsm);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+
|