mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
* Further work on Battle AI. Now it is able to cast a number of offensive spells. Battle callback exposes more spell-casting info.
* Took down the one Boost.Assign usage offending VC11. I'm getting impatient I guess...
This commit is contained in:
parent
6a81c8b1af
commit
95b866c131
@ -15,6 +15,8 @@ CBattleCallback * cbc;
|
||||
#define LOGL(text) print(text)
|
||||
#define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
|
||||
|
||||
|
||||
|
||||
class StackWithBonuses : public IBonusBearer
|
||||
{
|
||||
public:
|
||||
@ -238,6 +240,21 @@ struct AttackPossibility
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Key, typename Val>
|
||||
const Val &getValOr(const std::map<Key, Val> &Map, const Key &key, const Key &defaultValue)
|
||||
{
|
||||
auto i = Map.find(key);
|
||||
if(i != Map.end())
|
||||
return i->second;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
struct HypotheticChangesToBattleState
|
||||
{
|
||||
std::map<const CStack *, const IBonusBearer *> bonusesOfStacks;
|
||||
};
|
||||
|
||||
struct PotentialTargets
|
||||
{
|
||||
std::vector<AttackPossibility> possibleAttacks;
|
||||
@ -245,7 +262,10 @@ struct PotentialTargets
|
||||
|
||||
std::function<AttackPossibility(bool,BattleHex)> GenerateAttackInfo; //args: shooting, destHex
|
||||
|
||||
PotentialTargets(const CStack *attacker, optional<IBonusBearer*> attackerBonuses = boost::none)
|
||||
PotentialTargets()
|
||||
{}
|
||||
|
||||
PotentialTargets(const CStack *attacker, const HypotheticChangesToBattleState &state = HypotheticChangesToBattleState())
|
||||
{
|
||||
auto dists = cbc->battleGetDistances(attacker);
|
||||
std::vector<BattleHex> avHexes = cbc->battleGetAvailableHexes(attacker, false);
|
||||
@ -259,8 +279,8 @@ struct PotentialTargets
|
||||
GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility
|
||||
{
|
||||
auto bai = BattleAttackInfo(attacker, enemy, shooting);
|
||||
if(attackerBonuses)
|
||||
bai.attackerBonuses = *attackerBonuses;
|
||||
bai.attackerBonuses = getValOr(state.bonusesOfStacks, bai.attacker, bai.attacker);
|
||||
bai.defenderBonuses = getValOr(state.bonusesOfStacks, bai.defender, bai.defender);
|
||||
|
||||
AttackPossibility ap = {enemy, hex, bai, 0, 0};
|
||||
if(hex.isValid())
|
||||
@ -495,11 +515,32 @@ BattleAction CBattleAI::useCatapult(const CStack * stack)
|
||||
throw std::runtime_error("The method or operation is not implemented.");
|
||||
}
|
||||
|
||||
bool isSupportedSpell(const CSpell *spell)
|
||||
enum SpellTypes
|
||||
{
|
||||
OFFENSIVE_SPELL, TIMED_EFFECT, OTHER
|
||||
};
|
||||
|
||||
SpellTypes spellType(const CSpell *spell)
|
||||
{
|
||||
switch(spell->id)
|
||||
{
|
||||
// permanent effects
|
||||
//offense spell
|
||||
case Spells::MAGIC_ARROW:
|
||||
case Spells::ICE_BOLT:
|
||||
case Spells::LIGHTNING_BOLT:
|
||||
case Spells::IMPLOSION:
|
||||
case Spells::CHAIN_LIGHTNING:
|
||||
case Spells::FROST_RING:
|
||||
case Spells::FIREBALL:
|
||||
case Spells::INFERNO:
|
||||
case Spells::METEOR_SHOWER:
|
||||
case Spells::DEATH_RIPPLE:
|
||||
case Spells::DESTROY_UNDEAD:
|
||||
case Spells::ARMAGEDDON:
|
||||
case Spells::TITANS_LIGHTNING_BOLT:
|
||||
case Spells::THUNDERBOLT: //(thunderbirds)
|
||||
return OFFENSIVE_SPELL;
|
||||
|
||||
case Spells::SHIELD:
|
||||
case Spells::AIR_SHIELD:
|
||||
case Spells::FIRE_SHIELD:
|
||||
@ -537,10 +578,10 @@ bool isSupportedSpell(const CSpell *spell)
|
||||
case Spells::PARALYZE:
|
||||
case Spells::AGE:
|
||||
case Spells::ACID_BREATH_DEFENSE:
|
||||
return true;
|
||||
return TIMED_EFFECT;
|
||||
|
||||
default:
|
||||
return false;
|
||||
return OTHER;
|
||||
}
|
||||
};
|
||||
|
||||
@ -550,10 +591,55 @@ struct PossibleSpellcast
|
||||
BattleHex dest;
|
||||
};
|
||||
|
||||
struct CurrentOffensivePotential
|
||||
{
|
||||
std::map<const CStack *, PotentialTargets> ourAttacks;
|
||||
std::map<const CStack *, PotentialTargets> enemyAttacks;
|
||||
|
||||
CurrentOffensivePotential(ui8 side)
|
||||
{
|
||||
BOOST_FOREACH(auto stack, cbc->battleGetStacks())
|
||||
{
|
||||
if(stack->attackerOwned == !side)
|
||||
ourAttacks[stack] = PotentialTargets(stack);
|
||||
else
|
||||
enemyAttacks[stack] = PotentialTargets(stack);
|
||||
}
|
||||
}
|
||||
|
||||
int potentialValue()
|
||||
{
|
||||
int ourPotential = 0, enemyPotential = 0;
|
||||
BOOST_FOREACH(auto &p, ourAttacks)
|
||||
ourPotential += p.second.bestAction().attackValue();
|
||||
|
||||
BOOST_FOREACH(auto &p, enemyAttacks)
|
||||
enemyPotential += p.second.bestAction().attackValue();
|
||||
|
||||
return ourPotential - enemyPotential;
|
||||
}
|
||||
};
|
||||
//
|
||||
// //set has its own order, so remove_if won't work. TODO - reuse for map
|
||||
// template<typename Elem, typename Predicate>
|
||||
// void erase_if(std::set<Elem> &setContainer, Predicate pred)
|
||||
// {
|
||||
// auto itr = setContainer.begin();
|
||||
// auto endItr = setContainer.end();
|
||||
// while(itr != endItr)
|
||||
// {
|
||||
// auto tmpItr = itr++;
|
||||
// if(pred(*tmpItr))
|
||||
// setContainer.erase(tmpItr);
|
||||
// }
|
||||
// }
|
||||
|
||||
void CBattleAI::attemptCastingSpell()
|
||||
{
|
||||
LOGL("Casting spells sounds like fun. Let's see...");
|
||||
|
||||
auto hero = cb->battleGetMyHero();
|
||||
|
||||
//auto known = cb->battleGetFightingHero(side);
|
||||
|
||||
//Get all spells we can cast
|
||||
@ -566,14 +652,14 @@ void CBattleAI::attemptCastingSpell()
|
||||
LOGFL("I can cast %d spells.", possibleSpells.size());
|
||||
|
||||
vstd::erase_if(possibleSpells, [](const CSpell *s)
|
||||
{return !isSupportedSpell(s); });
|
||||
{return spellType(s) == OTHER; });
|
||||
LOGFL("I know about workings of %d of them.", possibleSpells.size());
|
||||
|
||||
//Get possible spell-target pairs
|
||||
std::vector<PossibleSpellcast> possibleCasts;
|
||||
BOOST_FOREACH(auto spell, possibleSpells)
|
||||
{
|
||||
BOOST_FOREACH(auto hex, cbc->battleGetPossibleTargets(playerID, spell))
|
||||
BOOST_FOREACH(auto hex, getTargetsToConsider(spell))
|
||||
{
|
||||
PossibleSpellcast ps = {spell, hex};
|
||||
possibleCasts.push_back(ps);
|
||||
@ -592,33 +678,77 @@ void CBattleAI::attemptCastingSpell()
|
||||
|
||||
auto evaluateSpellcast = [&] (const PossibleSpellcast &ps) -> int
|
||||
{
|
||||
int skillLevel = 0;
|
||||
const int skillLevel = hero->getSpellSchoolLevel(ps.spell);
|
||||
const int spellPower = hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
|
||||
|
||||
StackWithBonuses swb;
|
||||
swb.stack = cb->battleGetStackByPos(ps.dest);
|
||||
if(!swb.stack)
|
||||
return -1;
|
||||
switch(spellType(ps.spell))
|
||||
{
|
||||
case OFFENSIVE_SPELL:
|
||||
{
|
||||
int damageDealt = 0, damageReceived = 0;
|
||||
|
||||
Bonus pseudoBonus;
|
||||
pseudoBonus.sid = ps.spell->id;
|
||||
pseudoBonus.val = skillLevel;
|
||||
pseudoBonus.turnsRemain = 1; //TODO
|
||||
CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus);
|
||||
auto stacksSuffering = cb->getAffectedCreatures(ps.spell, skillLevel, playerID, ps.dest);
|
||||
vstd::erase_if(stacksSuffering, [&](const CStack *stack) -> bool
|
||||
{
|
||||
return cb->battleIsImmune(hero, ps.spell, ECastingMode::HERO_CASTING, ps.dest);
|
||||
});
|
||||
|
||||
PotentialTargets pt(swb.stack, &swb);
|
||||
auto newValue = pt.bestAction().attackValue();
|
||||
auto oldValue = valueOfStack[swb.stack];
|
||||
auto gain = newValue - oldValue;
|
||||
if(swb.stack->owner != playerID) //enemy
|
||||
gain = -gain;
|
||||
if(stacksSuffering.empty())
|
||||
return -1;
|
||||
|
||||
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));
|
||||
BOOST_FOREACH(auto stack, stacksSuffering)
|
||||
{
|
||||
const int dmg = cb->calculateSpellDmg(ps.spell, hero, stack, skillLevel, spellPower);
|
||||
if(stack->owner == playerID)
|
||||
damageReceived += dmg;
|
||||
else
|
||||
damageDealt += dmg;
|
||||
}
|
||||
|
||||
return gain;
|
||||
const int damageDiff = damageDealt - damageReceived;
|
||||
|
||||
|
||||
LOGFL("Casting %s on hex %d would deal %d damage points among %d stacks.",
|
||||
ps.spell->name % ps.dest % damageDiff % stacksSuffering.size());
|
||||
//TODO tactic effect too
|
||||
return damageDiff;
|
||||
}
|
||||
case TIMED_EFFECT:
|
||||
{
|
||||
StackWithBonuses swb;
|
||||
swb.stack = cb->battleGetStackByPos(ps.dest);
|
||||
if(!swb.stack)
|
||||
return -1;
|
||||
|
||||
Bonus pseudoBonus;
|
||||
pseudoBonus.sid = ps.spell->id;
|
||||
pseudoBonus.val = skillLevel;
|
||||
pseudoBonus.turnsRemain = 1; //TODO
|
||||
CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus);
|
||||
|
||||
HypotheticChangesToBattleState state;
|
||||
state.bonusesOfStacks[swb.stack] = &swb;
|
||||
|
||||
PotentialTargets pt(swb.stack, state);
|
||||
auto newValue = pt.bestAction().attackValue();
|
||||
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 % swb.stack->nodeName() % gain % (oldValue) % (newValue));
|
||||
|
||||
return gain;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
auto castToPerform = *vstd::maxElementByFun(possibleCasts, evaluateSpellcast);
|
||||
LOGFL("Best spell is %s. Will cast.", castToPerform.spell->name);
|
||||
|
||||
BattleAction spellcast;
|
||||
spellcast.actionType = BattleAction::HERO_SPELL;
|
||||
spellcast.additionalInfo = castToPerform.spell->id;
|
||||
@ -627,4 +757,24 @@ void CBattleAI::attemptCastingSpell()
|
||||
spellcast.stackNumber = (!side) ? -1 : -2;
|
||||
|
||||
cb->battleMakeAction(&spellcast);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<BattleHex> CBattleAI::getTargetsToConsider( const CSpell *spell ) const
|
||||
{
|
||||
if(spell->getTargetType() == CSpell::NO_TARGET)
|
||||
{
|
||||
//Spell can be casted anywhere, all hexes are potentially considerable.
|
||||
std::vector<BattleHex> ret;
|
||||
|
||||
for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
|
||||
if(BattleHex(i).isAvailable())
|
||||
ret.push_back(i);
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO when massive effect -> doesnt matter where cast
|
||||
return cbc->battleGetPossibleTargets(playerID, spell);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "../../lib/BattleHex.h"
|
||||
|
||||
class CSpell;
|
||||
|
||||
class CBattleAI : public CBattleGameInterface
|
||||
{
|
||||
int side;
|
||||
@ -38,5 +40,6 @@ public:
|
||||
BattleAction useCatapult(const CStack * stack);
|
||||
|
||||
void attemptCastingSpell();
|
||||
std::vector<BattleHex> getTargetsToConsider(const CSpell *spell) const;
|
||||
};
|
||||
|
||||
|
14
Global.h
14
Global.h
@ -371,6 +371,20 @@ namespace vstd
|
||||
vec.erase(boost::remove_if(vec, pred),vec.end());
|
||||
}
|
||||
|
||||
//set has its own order, so remove_if won't work. TODO - reuse for map
|
||||
template<typename Elem, typename Predicate>
|
||||
void erase_if(std::set<Elem> &setContainer, Predicate pred)
|
||||
{
|
||||
auto itr = setContainer.begin();
|
||||
auto endItr = setContainer.end();
|
||||
while(itr != endItr)
|
||||
{
|
||||
auto tmpItr = itr++;
|
||||
if(pred(*tmpItr))
|
||||
setContainer.erase(tmpItr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputRange, typename OutputIterator, typename Predicate>
|
||||
OutputIterator copy_if(const InputRange &input, OutputIterator result, Predicate pred)
|
||||
{
|
||||
|
@ -2274,9 +2274,15 @@ void CBattleInterface::showPieceOfWall(SDL_Surface * to, int hex, const std::vec
|
||||
{165, std::list<int>{11}}, {186, std::list<int>{3}}};
|
||||
#else
|
||||
using namespace boost::assign;
|
||||
static const std::map<int, std::list<int> > hexToPart = map_list_of<int, std::list<int> >(12, list_of<int>(8)(1)(7))(45, list_of<int>(12)(6))
|
||||
(101, list_of<int>(10))(118, list_of<int>(2))(165, list_of<int>(11))(186, list_of<int>(3));
|
||||
std::map<int, std::list<int> > hexToPart;
|
||||
hexToPart[12] = list_of<int>(8)(1)(7);
|
||||
hexToPart[45] = list_of<int>(12)(6);
|
||||
hexToPart[101] = list_of<int>(10);
|
||||
hexToPart[118] = list_of<int>(2);
|
||||
hexToPart[165] = list_of<int>(11);
|
||||
hexToPart[186] = list_of<int>(3);
|
||||
#endif
|
||||
|
||||
std::map<int, std::list<int> >::const_iterator it = hexToPart.find(hex);
|
||||
if(it != hexToPart.end())
|
||||
{
|
||||
|
@ -116,6 +116,7 @@ void CClient::waitForMoveAndSend(int color)
|
||||
{
|
||||
try
|
||||
{
|
||||
setThreadName("CClient::waitForMoveAndSend");
|
||||
assert(vstd::contains(battleints, color));
|
||||
BattleAction ba = battleints[color]->activeStack(gs->curB->battleGetStackByID(gs->curB->activeStack, false));
|
||||
MakeAction temp_action(ba);
|
||||
|
@ -104,3 +104,45 @@ bool BattleHex::isAvailable() const
|
||||
{
|
||||
return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH-1;
|
||||
}
|
||||
|
||||
BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities)
|
||||
{
|
||||
std::vector<BattleHex> sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :(
|
||||
|
||||
BattleHex initialHex = BattleHex(initialPos);
|
||||
auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool
|
||||
{
|
||||
return initialHex.getDistance (initialHex, left) < initialHex.getDistance (initialHex, right);
|
||||
};
|
||||
|
||||
boost::sort (sortedTiles, compareDistance); //closest tiles at front
|
||||
|
||||
int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
|
||||
|
||||
auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
|
||||
{
|
||||
return closestDistance < here.getDistance (initialPos, here);
|
||||
};
|
||||
|
||||
vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
|
||||
|
||||
auto compareHorizontal = [attackerOwned, initialPos](const BattleHex left, const BattleHex right) -> bool
|
||||
{
|
||||
if(left.getX() != right.getX())
|
||||
{
|
||||
if (attackerOwned)
|
||||
return left.getX() > right.getX(); //find furthest right
|
||||
else
|
||||
return left.getX() < right.getX(); //find furthest left
|
||||
}
|
||||
else
|
||||
{
|
||||
//Prefer tiles in the same row.
|
||||
return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
|
||||
}
|
||||
};
|
||||
|
||||
boost::sort (sortedTiles, compareHorizontal);
|
||||
|
||||
return sortedTiles.front();
|
||||
}
|
@ -111,4 +111,5 @@ struct DLL_LINKAGE BattleHex
|
||||
static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret);
|
||||
|
||||
bool isAvailable() const; //valid position not in first or last column
|
||||
static BattleHex getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
|
||||
};
|
@ -36,48 +36,6 @@ const CStack * BattleInfo::getNextStack() const
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BattleHex BattleInfo::getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities) const
|
||||
{
|
||||
std::vector<BattleHex> sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :(
|
||||
|
||||
BattleHex initialHex = BattleHex(initialPos);
|
||||
auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool
|
||||
{
|
||||
return initialHex.getDistance (initialHex, left) < initialHex.getDistance (initialHex, right);
|
||||
};
|
||||
|
||||
boost::sort (sortedTiles, compareDistance); //closest tiles at front
|
||||
|
||||
int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
|
||||
|
||||
auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
|
||||
{
|
||||
return closestDistance < here.getDistance (initialPos, here);
|
||||
};
|
||||
|
||||
vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
|
||||
|
||||
auto compareHorizontal = [attackerOwned, initialPos](const BattleHex left, const BattleHex right) -> bool
|
||||
{
|
||||
if(left.getX() != right.getX())
|
||||
{
|
||||
if (attackerOwned)
|
||||
return left.getX() > right.getX(); //find furthest right
|
||||
else
|
||||
return left.getX() < right.getX(); //find furthest left
|
||||
}
|
||||
else
|
||||
{
|
||||
//Prefer tiles in the same row.
|
||||
return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
|
||||
}
|
||||
};
|
||||
|
||||
boost::sort (sortedTiles, compareHorizontal);
|
||||
|
||||
return sortedTiles.front();
|
||||
}
|
||||
|
||||
int BattleInfo::getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos) const
|
||||
{
|
||||
bool twoHex = VLC->creh->creatures[creID]->isDoubleWide();
|
||||
@ -106,7 +64,7 @@ int BattleInfo::getAvaliableHex(TCreature creID, bool attackerOwned, int initial
|
||||
return BattleHex::INVALID; //all tiles are covered
|
||||
}
|
||||
|
||||
return getClosestTile(attackerOwned, pos, occupyable);
|
||||
return BattleHex::getClosestTile(attackerOwned, pos, occupyable);
|
||||
}
|
||||
|
||||
std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack *stack)
|
||||
@ -162,111 +120,6 @@ void BattleInfo::calculateCasualties( std::map<ui32,si32> *casualties ) const
|
||||
}
|
||||
}
|
||||
|
||||
std::set<const CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, BattleHex destinationTile)
|
||||
{
|
||||
std::set<const CStack*> attackedCres; /*std::set to exclude multiple occurrences of two hex creatures*/
|
||||
|
||||
const ui8 attackerSide = sides[1] == attackerOwner;
|
||||
const auto attackedHexes = s->rangeInHexes(destinationTile, skillLevel, attackerSide);
|
||||
const bool onlyAlive = s->id != Spells::RESURRECTION && s->id != Spells::ANIMATE_DEAD; //when casting resurrection or animate dead we should be allow to select dead stack
|
||||
//fixme: what about other rising spells (Sacrifice) ?
|
||||
if(s->id == Spells::DEATH_RIPPLE || s->id == Spells::DESTROY_UNDEAD || s->id == Spells::ARMAGEDDON)
|
||||
{
|
||||
for(int it=0; it<stacks.size(); ++it)
|
||||
{
|
||||
if((s->id == Spells::DEATH_RIPPLE && !stacks[it]->getCreature()->isUndead()) //death ripple
|
||||
|| (s->id == Spells::DESTROY_UNDEAD && stacks[it]->getCreature()->isUndead()) //destroy undead
|
||||
|| (s->id == Spells::ARMAGEDDON) //Armageddon
|
||||
)
|
||||
{
|
||||
if(stacks[it]->isValidTarget())
|
||||
attackedCres.insert(stacks[it]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s->id == Spells::CHAIN_LIGHTNING)
|
||||
{
|
||||
std::set<BattleHex> possibleHexes;
|
||||
BOOST_FOREACH (auto stack, stacks)
|
||||
{
|
||||
if (stack->isValidTarget())
|
||||
{
|
||||
BOOST_FOREACH (auto hex, stack->getHexes())
|
||||
{
|
||||
possibleHexes.insert (hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
BattleHex lightningHex = destinationTile;
|
||||
for (int i = 0; i < 5; ++i) //TODO: depends on spell school level
|
||||
{
|
||||
auto stack = battleGetStackByPos (lightningHex, true);
|
||||
if (!stack)
|
||||
break;
|
||||
attackedCres.insert (stack);
|
||||
BOOST_FOREACH (auto hex, stack->getHexes())
|
||||
{
|
||||
possibleHexes.erase (hex); //can't hit same place twice
|
||||
}
|
||||
lightningHex = getClosestTile (attackerOwner, destinationTile, possibleHexes);
|
||||
}
|
||||
}
|
||||
else if (s->range[skillLevel].size() > 1) //custom many-hex range
|
||||
{
|
||||
BOOST_FOREACH(BattleHex hex, attackedHexes)
|
||||
{
|
||||
if(const CStack * st = battleGetStackByPos(hex, onlyAlive))
|
||||
{
|
||||
if (s->id == 76) //Death Cloud //TODO: fireball and fire immunity
|
||||
{
|
||||
if (st->isLiving() || st->coversPos(destinationTile)) //directly hit or alive
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
else
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(s->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
|
||||
{
|
||||
if(skillLevel < 3) /*not expert */
|
||||
{
|
||||
const CStack * st = battleGetStackByPos(destinationTile, onlyAlive);
|
||||
if(st)
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int it=0; it<stacks.size(); ++it)
|
||||
{
|
||||
/*if it's non negative spell and our unit or non positive spell and hostile unit */
|
||||
if((!s->isNegative() && stacks[it]->owner == attackerOwner)
|
||||
||(!s->isPositive() && stacks[it]->owner != attackerOwner )
|
||||
)
|
||||
{
|
||||
if(stacks[it]->isValidTarget(!onlyAlive))
|
||||
attackedCres.insert(stacks[it]);
|
||||
}
|
||||
}
|
||||
} //if(caster->getSpellSchoolLevel(s) < 3)
|
||||
}
|
||||
else if(s->getTargetType() == CSpell::CREATURE)
|
||||
{
|
||||
if(const CStack * st = battleGetStackByPos(destinationTile, onlyAlive))
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
else //custom range from attackedHexes
|
||||
{
|
||||
BOOST_FOREACH(BattleHex hex, attackedHexes)
|
||||
{
|
||||
if(const CStack * st = battleGetStackByPos(hex, onlyAlive))
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
return attackedCres;
|
||||
}
|
||||
|
||||
int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower)
|
||||
{
|
||||
@ -309,85 +162,6 @@ CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool at
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui32 BattleInfo::calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const
|
||||
{
|
||||
ui32 ret = baseDamage;
|
||||
//applying sorcery secondary skill
|
||||
if(caster)
|
||||
{
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::SORCERY)) / 100.0;
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, sp->id)) / 100.0;
|
||||
|
||||
if(sp->air)
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::AIR_SPELL_DMG_PREMY)) / 100.0;
|
||||
else if(sp->fire) //only one type of bonus for Magic Arrow
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::FIRE_SPELL_DMG_PREMY)) / 100.0;
|
||||
else if(sp->water)
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::WATER_SPELL_DMG_PREMY)) / 100.0;
|
||||
else if(sp->earth)
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::EARTH_SPELL_DMG_PREMY)) / 100.0;
|
||||
|
||||
if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
|
||||
ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, sp->id) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui32 BattleInfo::calculateSpellDmg( const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower ) const
|
||||
{
|
||||
ui32 ret = 0; //value to return
|
||||
|
||||
//15 - magic arrows, 16 - ice bolt, 17 - lightning bolt, 18 - implosion, 20 - frost ring, 21 - fireball, 22 - inferno, 23 - meteor shower,
|
||||
//24 - death ripple, 25 - destroy undead, 26 - armageddon, 77 - thunderbolt
|
||||
|
||||
//check if spell really does damage - if not, return 0
|
||||
if(VLC->spellh->damageSpells.find(sp->id) == VLC->spellh->damageSpells.end())
|
||||
return 0;
|
||||
|
||||
ret = usedSpellPower * sp->power;
|
||||
ret += sp->powers[spellSchoolLevel];
|
||||
|
||||
//affected creature-specific part
|
||||
if(affectedCreature)
|
||||
{
|
||||
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
||||
if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 0)) //air spell & protection from air
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 0);
|
||||
ret /= 100;
|
||||
}
|
||||
else if(sp->fire && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 1)) //fire spell & protection from fire
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 1);
|
||||
ret /= 100;
|
||||
}
|
||||
else if(sp->water && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 2)) //water spell & protection from water
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 2);
|
||||
ret /= 100;
|
||||
}
|
||||
else if (sp->earth && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 3)) //earth spell & protection from earth
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 3);
|
||||
ret /= 100;
|
||||
}
|
||||
//general spell dmg reduction
|
||||
if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1)) //air spell & protection from air
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
|
||||
ret /= 100;
|
||||
}
|
||||
//dmg increasing
|
||||
if( affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id) )
|
||||
{
|
||||
ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id);
|
||||
ret /= 100;
|
||||
}
|
||||
}
|
||||
ret = calculateSpellBonus(ret, sp, caster, affectedCreature);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack) const
|
||||
{
|
||||
bool resurrect = resurrects(spell->id);
|
||||
|
@ -90,7 +90,6 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
|
||||
|
||||
//void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<BattleHex> & occupyable, bool flying, const CStack* stackToOmmit = NULL) const; //send pointer to at least 187 allocated bytes
|
||||
//static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
|
||||
BattleHex getClosestTile (bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities) const; //TODO: vector or set? copying one to another is bad
|
||||
int getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects
|
||||
//void makeBFS(BattleHex start, bool*accessibility, BattleHex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
|
||||
std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const CStack *stack); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
|
||||
@ -102,8 +101,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
|
||||
|
||||
ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting)
|
||||
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
|
||||
using CBattleInfoCallback::getAttackedCreatures;
|
||||
std::set<const CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, BattleHex destinationTile); //calculates stack affected by given spell
|
||||
|
||||
//void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee
|
||||
//std::set<CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
|
||||
//std::set<BattleHex> getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
|
||||
@ -112,8 +110,6 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
|
||||
CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
|
||||
int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack
|
||||
//std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
|
||||
ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
|
||||
ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
|
||||
ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = NULL) const;
|
||||
ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel
|
||||
ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //unused
|
||||
|
@ -1816,6 +1816,189 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
||||
return battleIsImmune(NULL, spell, mode, dest);
|
||||
}
|
||||
|
||||
ui32 CBattleInfoCallback::calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const
|
||||
{
|
||||
ui32 ret = baseDamage;
|
||||
//applying sorcery secondary skill
|
||||
if(caster)
|
||||
{
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::SORCERY)) / 100.0;
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, sp->id)) / 100.0;
|
||||
|
||||
if(sp->air)
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::AIR_SPELL_DMG_PREMY)) / 100.0;
|
||||
else if(sp->fire) //only one type of bonus for Magic Arrow
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::FIRE_SPELL_DMG_PREMY)) / 100.0;
|
||||
else if(sp->water)
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::WATER_SPELL_DMG_PREMY)) / 100.0;
|
||||
else if(sp->earth)
|
||||
ret *= (100.0 + caster->valOfBonuses(Bonus::EARTH_SPELL_DMG_PREMY)) / 100.0;
|
||||
|
||||
if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
|
||||
ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, sp->id) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui32 CBattleInfoCallback::calculateSpellDmg( const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower ) const
|
||||
{
|
||||
ui32 ret = 0; //value to return
|
||||
|
||||
//check if spell really does damage - if not, return 0
|
||||
if(VLC->spellh->damageSpells.find(sp->id) == VLC->spellh->damageSpells.end())
|
||||
return 0;
|
||||
|
||||
ret = usedSpellPower * sp->power;
|
||||
ret += sp->powers[spellSchoolLevel];
|
||||
|
||||
//affected creature-specific part
|
||||
if(affectedCreature)
|
||||
{
|
||||
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
||||
if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 0)) //air spell & protection from air
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 0);
|
||||
ret /= 100;
|
||||
}
|
||||
else if(sp->fire && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 1)) //fire spell & protection from fire
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 1);
|
||||
ret /= 100;
|
||||
}
|
||||
else if(sp->water && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 2)) //water spell & protection from water
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 2);
|
||||
ret /= 100;
|
||||
}
|
||||
else if (sp->earth && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 3)) //earth spell & protection from earth
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 3);
|
||||
ret /= 100;
|
||||
}
|
||||
//general spell dmg reduction
|
||||
//FIXME?
|
||||
if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1))
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
|
||||
ret /= 100;
|
||||
}
|
||||
//dmg increasing
|
||||
if( affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id) )
|
||||
{
|
||||
ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id);
|
||||
ret /= 100;
|
||||
}
|
||||
}
|
||||
ret = calculateSpellBonus(ret, sp, caster, affectedCreature);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell * spell, int skillLevel, ui8 attackerOwner, BattleHex destinationTile)
|
||||
{
|
||||
std::set<const CStack*> attackedCres; /*std::set to exclude multiple occurrences of two hex creatures*/
|
||||
|
||||
const ui8 attackerSide = playerToSide(attackerOwner) == 1;
|
||||
const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide);
|
||||
const bool onlyAlive = spell->id != Spells::RESURRECTION && spell->id != Spells::ANIMATE_DEAD; //when casting resurrection or animate dead we should be allow to select dead stack
|
||||
//fixme: what about other rising spells (Sacrifice) ?
|
||||
if(spell->id == Spells::DEATH_RIPPLE || spell->id == Spells::DESTROY_UNDEAD || spell->id == Spells::ARMAGEDDON)
|
||||
{
|
||||
BOOST_FOREACH(const CStack *stack, battleGetAllStacks())
|
||||
{
|
||||
if((spell->id == Spells::DEATH_RIPPLE && !stack->getCreature()->isUndead()) //death ripple
|
||||
|| (spell->id == Spells::DESTROY_UNDEAD && stack->getCreature()->isUndead()) //destroy undead
|
||||
|| (spell->id == Spells::ARMAGEDDON) //Armageddon
|
||||
)
|
||||
{
|
||||
if(stack->isValidTarget())
|
||||
attackedCres.insert(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (spell->id == Spells::CHAIN_LIGHTNING)
|
||||
{
|
||||
std::set<BattleHex> possibleHexes;
|
||||
BOOST_FOREACH (auto stack, battleGetAllStacks())
|
||||
{
|
||||
if (stack->isValidTarget())
|
||||
{
|
||||
BOOST_FOREACH (auto hex, stack->getHexes())
|
||||
{
|
||||
possibleHexes.insert (hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
BattleHex lightningHex = destinationTile;
|
||||
for (int i = 0; i < 5; ++i) //TODO: depends on spell school level
|
||||
{
|
||||
auto stack = battleGetStackByPos (lightningHex, true);
|
||||
if (!stack)
|
||||
break;
|
||||
attackedCres.insert (stack);
|
||||
BOOST_FOREACH (auto hex, stack->getHexes())
|
||||
{
|
||||
possibleHexes.erase (hex); //can't hit same place twice
|
||||
}
|
||||
lightningHex = BattleHex::getClosestTile (attackerOwner, destinationTile, possibleHexes);
|
||||
}
|
||||
}
|
||||
else if (spell->range[skillLevel].size() > 1) //custom many-hex range
|
||||
{
|
||||
BOOST_FOREACH(BattleHex hex, attackedHexes)
|
||||
{
|
||||
if(const CStack * st = battleGetStackByPos(hex, onlyAlive))
|
||||
{
|
||||
if (spell->id == 76) //Death Cloud //TODO: fireball and fire immunity
|
||||
{
|
||||
if (st->isLiving() || st->coversPos(destinationTile)) //directly hit or alive
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
else
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
|
||||
{
|
||||
if(skillLevel < 3) /*not expert */
|
||||
{
|
||||
const CStack * st = battleGetStackByPos(destinationTile, onlyAlive);
|
||||
if(st)
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_FOREACH (auto stack, battleGetAllStacks())
|
||||
{
|
||||
/*if it's non negative spell and our unit or non positive spell and hostile unit */
|
||||
if((!spell->isNegative() && stack->owner == attackerOwner)
|
||||
||(!spell->isPositive() && stack->owner != attackerOwner )
|
||||
)
|
||||
{
|
||||
if(stack->isValidTarget(!onlyAlive))
|
||||
attackedCres.insert(stack);
|
||||
}
|
||||
}
|
||||
} //if(caster->getSpellSchoolLevel(s) < 3)
|
||||
}
|
||||
else if(spell->getTargetType() == CSpell::CREATURE)
|
||||
{
|
||||
if(const CStack * st = battleGetStackByPos(destinationTile, onlyAlive))
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
else //custom range from attackedHexes
|
||||
{
|
||||
BOOST_FOREACH(BattleHex hex, attackedHexes)
|
||||
{
|
||||
if(const CStack * st = battleGetStackByPos(hex, onlyAlive))
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
return attackedCres;
|
||||
}
|
||||
|
||||
const CStack * CBattleInfoCallback::getStackIf(boost::function<bool(const CStack*)> pred) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(nullptr);
|
||||
@ -2110,6 +2293,19 @@ bool CPlayerBattleCallback::battleCanCastSpell(ESpellCastProblem::ESpellCastProb
|
||||
return problem == ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const
|
||||
{
|
||||
return CBattleInfoEssentials::battleGetFightingHero(battleGetMySide());
|
||||
}
|
||||
|
||||
InfoAboutHero CPlayerBattleCallback::battleGetEnemyHero() const
|
||||
{
|
||||
InfoAboutHero ret;
|
||||
assert(0);
|
||||
///TODO implement and replace usages of battleGetFightingHero obtaining enemy hero
|
||||
return ret;
|
||||
}
|
||||
|
||||
BattleAttackInfo::BattleAttackInfo(const CStack *Attacker, const CStack *Defender, bool Shooting)
|
||||
{
|
||||
attacker = Attacker;
|
||||
|
@ -9,6 +9,7 @@ class CSpell;
|
||||
struct BattleInfo;
|
||||
struct CObstacleInstance;
|
||||
class IBonusBearer;
|
||||
struct InfoAboutHero;
|
||||
|
||||
namespace boost
|
||||
{class shared_mutex;}
|
||||
@ -243,6 +244,9 @@ public:
|
||||
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(int player, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode
|
||||
ESpellCastProblem::ESpellCastProblem battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination) const; //determines if creature can cast a spell here
|
||||
std::vector<BattleHex> battleGetPossibleTargets(int player, const CSpell *spell) const;
|
||||
ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
|
||||
ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
|
||||
std::set<const CStack*> getAffectedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, BattleHex destinationTile); //calculates stack affected by given spell
|
||||
|
||||
si32 battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const;
|
||||
TSpell getRandomBeneficialSpell(const CStack * subject) const;
|
||||
@ -294,4 +298,6 @@ public:
|
||||
int battleGetSurrenderCost() const; //returns cost of surrendering battle, -1 if surrendering is not possible
|
||||
|
||||
bool battleCanCastSpell(ESpellCastProblem::ESpellCastProblem *outProblem = nullptr) const; //returns true, if caller can cast a spell. If not, if pointer is given via arg, the reason will be written.
|
||||
const CGHeroInstance * battleGetMyHero() const;
|
||||
InfoAboutHero battleGetEnemyHero() const;
|
||||
};
|
||||
|
@ -2812,8 +2812,11 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname)
|
||||
{
|
||||
const JsonNode & spells = n["spells"];
|
||||
if(spells.getType() == JsonNode::DATA_STRING && spells.String() == "all")
|
||||
{
|
||||
BOOST_FOREACH(auto spell, VLC->spellh->spells)
|
||||
ss.spells.insert(spell->id);
|
||||
if(spell->id <= Spells::SUMMON_AIR_ELEMENTAL)
|
||||
ss.spells.insert(spell->id);
|
||||
}
|
||||
else
|
||||
BOOST_FOREACH(const JsonNode &spell, n["spells"].Vector())
|
||||
ss.spells.insert(spell.Float());
|
||||
|
@ -793,7 +793,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
||||
bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
|
||||
bat.bsa.front().effect = VLC->spellh->spells[bonus->subtype]->mainEffectAnim; //hopefully it does not interfere with any other effect?
|
||||
|
||||
std::set<const CStack*> attackedCreatures = gs->curB->getAttackedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, targetHex);
|
||||
std::set<const CStack*> attackedCreatures = gs->curB->getAffectedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, targetHex);
|
||||
//TODO: get exact attacked hex for defender
|
||||
|
||||
BOOST_FOREACH(const CStack * stack, attackedCreatures)
|
||||
@ -3941,7 +3941,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
||||
std::set<const CStack*> attackedCres;
|
||||
if (mode != ECastingMode::ENCHANTER_CASTING)
|
||||
{
|
||||
attackedCres = gs->curB->getAttackedCreatures(spell, spellLvl, casterColor, destination);
|
||||
attackedCres = gs->curB->getAffectedCreatures(spell, spellLvl, casterColor, destination);
|
||||
for(std::set<const CStack*>::const_iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
|
||||
{
|
||||
sc.affectedCres.insert((*it)->ID);
|
||||
|
Loading…
x
Reference in New Issue
Block a user