2011-12-14 00:23:17 +03:00
# include " StdInc.h "
2010-12-25 21:23:30 +02:00
# include "BattleState.h"
2011-12-14 00:23:17 +03:00
2010-12-25 21:23:30 +02:00
# include <numeric>
2011-07-08 17:54:20 +03:00
# include <boost/random/linear_congruential.hpp>
2010-12-25 21:23:30 +02:00
# include "VCMI_Lib.h"
# include "CObjectHandler.h"
# include "CHeroHandler.h"
# include "CCreatureHandler.h"
# include "CSpellHandler.h"
# include "CTownHandler.h"
2011-01-08 20:33:40 +02:00
# include "NetPacks.h"
2012-08-02 14:03:26 +03:00
# include "JsonNode.h"
# include "Filesystem/CResourceLoader.h"
2010-12-25 21:23:30 +02:00
2011-12-14 00:23:17 +03:00
2010-12-25 21:23:30 +02:00
/*
* BattleState . h , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2011-07-08 17:54:20 +03:00
extern boost : : rand48 ran ;
2010-12-25 21:23:30 +02:00
const CStack * BattleInfo : : getNextStack ( ) const
{
std : : vector < const CStack * > hlp ;
2012-08-26 12:07:48 +03:00
battleGetStackQueue ( hlp , 1 , - 1 ) ;
2010-12-25 21:23:30 +02:00
if ( hlp . size ( ) )
return hlp [ 0 ] ;
else
return NULL ;
}
2012-08-26 12:07:48 +03:00
// const CStack * BattleInfo::getStackT(BattleHex tileID, bool onlyAlive) const
// {
// return const_cast<BattleInfo * const>(this)->getStackT(tileID, onlyAlive);
// }
// void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<BattleHex> & occupyable, bool flying, const CStack * stackToOmmit) const
// {
// memset(accessibility, 1, GameConstants::BFIELD_SIZE); //initialize array with trues
//
// //removing accessibility for side columns of hexes
// for(int v = 0; v < GameConstants::BFIELD_SIZE; ++v)
// {
// if( v % GameConstants::BFIELD_WIDTH == 0 || v % GameConstants::BFIELD_WIDTH == (GameConstants::BFIELD_WIDTH - 1) )
// accessibility[v] = false;
// }
//
// for(ui32 g=0; g<stacks.size(); ++g)
// {
// if(!stacks[g]->alive() || (stackToOmmit && stacks[g]->ID==stackToOmmit->ID) || stacks[g]->position < 0) //we don't want to lock position of this stack (eg. if it's a turret)
// continue;
//
// accessibility[stacks[g]->position] = false;
// if(stacks[g]->doubleWide()) //if it's a double hex creature
// {
// if(stacks[g]->attackerOwned)
// accessibility[stacks[g]->position-1] = false;
// else
// accessibility[stacks[g]->position+1] = false;
// }
// }
// //obstacles
// BOOST_FOREACH(const auto &obstacle, obstacles)
// {
// BOOST_FOREACH(BattleHex hex, obstacle->getBlockedTiles())
// {
// assert(hex.isValid());
// accessibility[hex] = false;
// }
// }
//
// //walls
// if(siege > 0)
// {
// static const int permanentlyLocked[] = {12, 45, 78, 112, 147, 165};
// for(int b=0; b<ARRAY_COUNT(permanentlyLocked); ++b)
// {
// accessibility[permanentlyLocked[b]] = false;
// }
//
// static const std::pair<int, BattleHex> lockedIfNotDestroyed[] = //(which part of wall, which hex is blocked if this part of wall is not destroyed
// {std::make_pair(2, BattleHex(182)), std::make_pair(3, BattleHex(130)),
// std::make_pair(4, BattleHex(62)), std::make_pair(5, BattleHex(29))};
// for(int b=0; b<ARRAY_COUNT(lockedIfNotDestroyed); ++b)
// {
// if(si.wallState[lockedIfNotDestroyed[b].first] < 3)
// {
// accessibility[lockedIfNotDestroyed[b].second] = false;
// }
// }
//
// //gate
// if(attackerOwned && si.wallState[7] < 3) //if it attacker's unit and gate is not destroyed
// {
// accessibility[95] = accessibility[96] = false; //block gate's hexes
// }
// }
//
// //occupyability
// if(addOccupiable && twoHex)
// {
// std::set<BattleHex> rem; //tiles to unlock
// for(int h=0; h<GameConstants::BFIELD_HEIGHT; ++h)
// {
// for(int w=1; w<GameConstants::BFIELD_WIDTH-1; ++w)
// {
// BattleHex hex(w, h);
// if(!isAccessible(hex, accessibility, twoHex, attackerOwned, flying, true)
// && (attackerOwned ? isAccessible(hex+1, accessibility, twoHex, attackerOwned, flying, true) : isAccessible(hex-1, accessibility, twoHex, attackerOwned, flying, true) )
// )
// rem.insert(hex);
// }
// }
// occupyable = rem;
// /*for(std::set<int>::const_iterator it = rem.begin(); it != rem.end(); ++it)
// {
// accessibility[*it] = true;
// }*/
// }
// }
// bool BattleInfo::isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos)
// {
// if(flying && !lastPos)
// return true;
//
// if(twoHex)
// {
// //if given hex is accessible and appropriate adjacent one is free too
// return accessibility[hex] && accessibility[hex + (attackerOwned ? -1 : 1 )];
// }
// else
// {
// return accessibility[hex];
// }
// }
// void BattleInfo::makeBFS(BattleHex start, bool *accessibility, BattleHex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const //both pointers must point to the at least 187-elements int arrays
// {
// std::set<BattleHex> quicksands = getStoppers(!attackerOwned);
//
// //inits
// for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
// predecessor[b] = -1;
// for(int g=0; g<GameConstants::BFIELD_SIZE; ++g)
// dists[g] = 100000000;
//
// std::queue< std::pair<BattleHex, bool> > hexq; //bfs queue <hex, accessible> (second filed used only if fillPredecessors is true)
// hexq.push(std::make_pair(start, true));
// dists[hexq.front().first] = 0;
// int curNext = -1; //for bfs loop only (helper var)
// while(!hexq.empty()) //bfs loop
// {
// std::pair<BattleHex, bool> curHex = hexq.front();
// std::vector<BattleHex> neighbours = curHex.first.neighbouringTiles();
// hexq.pop();
// if(curHex.first != start && !flying && vstd::contains(quicksands, curHex.first)) //walking stack can't step past the quicksands
// continue;
//
// for(ui32 nr=0; nr<neighbours.size(); nr++)
// {
// curNext = neighbours[nr]; //if(!accessibility[curNext] || (dists[curHex]+1)>=dists[curNext])
// bool accessible = isAccessible(curNext, accessibility, twoHex, attackerOwned, flying, dists[curHex.first]+1 == dists[curNext]);
// if( dists[curHex.first]+1 >= dists[curNext] )
// continue;
// if(accessible && curHex.second)
// {
// hexq.push(std::make_pair(curNext, true));
// dists[curNext] = dists[curHex.first] + 1;
// }
// else if(fillPredecessors && !(accessible && !curHex.second))
// {
// hexq.push(std::make_pair(curNext, false));
// dists[curNext] = dists[curHex.first] + 1;
// }
// predecessor[curNext] = curHex.first;
// }
// }
// };
//
2011-10-01 22:56:54 +03:00
2012-04-22 20:38:36 +03:00
BattleHex BattleInfo : : getClosestTile ( bool attackerOwned , int initialPos , std : : set < BattleHex > & possibilities ) const
{
std : : vector < BattleHex > sortedTiles ( possibilities . begin ( ) , possibilities . end ( ) ) ; //set can't be sorted properly :(
BattleHex initialHex = BattleHex ( initialPos ) ;
2012-05-20 14:40:23 +03:00
auto compareDistance = [ initialHex ] ( const BattleHex left , const BattleHex right ) - > bool
2012-04-22 20:38:36 +03:00
{
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
{
2012-05-22 13:15:16 +03:00
return closestDistance < here . getDistance ( initialPos , here ) ;
2012-04-22 20:38:36 +03:00
} ;
2012-05-20 14:40:23 +03:00
sortedTiles . erase ( boost : : remove_if ( sortedTiles , notClosest ) , sortedTiles . end ( ) ) ; //only closest tiles are interesting
2012-04-22 20:38:36 +03:00
auto compareHorizontal = [ attackerOwned ] ( const BattleHex left , const BattleHex right ) - > bool
{
if ( attackerOwned )
return left . getX ( ) > right . getX ( ) ; //find furthest right
else
return left . getX ( ) < right . getX ( ) ; //find furthest left
} ;
boost : : sort ( sortedTiles , compareHorizontal ) ;
return sortedTiles . front ( ) ;
}
2011-10-01 22:56:54 +03:00
int BattleInfo : : getAvaliableHex ( TCreature creID , bool attackerOwned , int initialPos ) const
{
bool twoHex = VLC - > creh - > creatures [ creID ] - > isDoubleWide ( ) ;
2012-06-09 22:24:04 +03:00
bool flying = VLC - > creh - > creatures [ creID ] - > isFlying ( ) ;
2012-06-16 17:25:39 +03:00
int pos ;
if ( initialPos > - 1 )
pos = initialPos ;
else //summon elementals depending on player side
{
if ( attackerOwned )
pos = 0 ; //top left
else
pos = GameConstants : : BFIELD_WIDTH - 1 ; //top right
}
2012-08-26 12:07:48 +03:00
auto accessibility = getAccesibility ( ) ;
2012-06-09 22:24:04 +03:00
std : : set < BattleHex > occupyable ;
2012-08-26 12:07:48 +03:00
for ( int i = 0 ; i < accessibility . size ( ) ; i + + )
if ( accessibility . accessible ( i , twoHex , attackerOwned ) )
occupyable . insert ( i ) ;
2012-04-22 20:38:36 +03:00
if ( ! occupyable . size ( ) )
2010-12-25 21:23:30 +02:00
{
2012-08-26 12:07:48 +03:00
return BattleHex : : INVALID ; //all tiles are covered
2010-12-25 21:23:30 +02:00
}
2012-08-26 12:07:48 +03:00
return getClosestTile ( attackerOwned , pos , occupyable ) ;
2010-12-25 21:23:30 +02:00
}
2012-08-26 12:07:48 +03:00
std : : pair < std : : vector < BattleHex > , int > BattleInfo : : getPath ( BattleHex start , BattleHex dest , const CStack * stack )
2011-01-07 12:48:31 +02:00
{
2012-08-26 12:07:48 +03:00
auto reachability = getReachability ( stack ) ;
2010-12-25 21:23:30 +02:00
2012-08-26 12:07:48 +03:00
if ( reachability . predecessors [ dest ] = = - 1 ) //cannot reach destination
2010-12-25 21:23:30 +02:00
{
2011-12-22 16:05:19 +03:00
return std : : make_pair ( std : : vector < BattleHex > ( ) , 0 ) ;
2010-12-25 21:23:30 +02:00
}
//making the Path
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > path ;
BattleHex curElem = dest ;
2010-12-25 21:23:30 +02:00
while ( curElem ! = start )
{
path . push_back ( curElem ) ;
2012-08-26 12:07:48 +03:00
curElem = reachability . predecessors [ curElem ] ;
2010-12-25 21:23:30 +02:00
}
2012-08-26 12:07:48 +03:00
return std : : make_pair ( path , reachability . distances [ dest ] ) ;
2011-01-08 20:33:40 +02:00
}
2011-07-06 20:00:45 +03:00
ui32 BattleInfo : : calculateDmg ( const CStack * attacker , const CStack * defender , const CGHeroInstance * attackerHero , const CGHeroInstance * defendingHero ,
bool shooting , ui8 charge , bool lucky , bool deathBlow , bool ballistaDoubleDmg )
2010-12-25 21:23:30 +02:00
{
2012-08-26 12:07:48 +03:00
TDmgRange range = calculateDmgRange ( attacker , defender , shooting , charge , lucky , deathBlow , ballistaDoubleDmg ) ;
2010-12-25 21:23:30 +02:00
if ( range . first ! = range . second )
{
int valuesToAverage [ 10 ] ;
int howManyToAv = std : : min < ui32 > ( 10 , attacker - > count ) ;
for ( int g = 0 ; g < howManyToAv ; + + g )
{
valuesToAverage [ g ] = range . first + rand ( ) % ( range . second - range . first + 1 ) ;
}
return std : : accumulate ( valuesToAverage , valuesToAverage + howManyToAv , 0 ) / howManyToAv ;
}
else
return range . first ;
}
void BattleInfo : : calculateCasualties ( std : : map < ui32 , si32 > * casualties ) const
{
2011-12-14 00:23:17 +03:00
for ( ui32 i = 0 ; i < stacks . size ( ) ; i + + ) //setting casualties
2010-12-25 21:23:30 +02:00
{
const CStack * const st = stacks [ i ] ;
si32 killed = ( st - > alive ( ) ? st - > baseAmount - st - > count : st - > baseAmount ) ;
2011-12-14 00:23:17 +03:00
vstd : : amax ( killed , 0 ) ;
2010-12-25 21:23:30 +02:00
if ( killed )
casualties [ ! st - > attackerOwned ] [ st - > getCreature ( ) - > idNumber ] + = killed ;
}
}
2012-08-26 12:07:48 +03:00
std : : set < const CStack * > BattleInfo : : getAttackedCreatures ( const CSpell * s , int skillLevel , ui8 attackerOwner , BattleHex destinationTile )
2010-12-25 21:23:30 +02:00
{
2012-08-26 12:07:48 +03:00
std : : set < const CStack * > attackedCres ; /*std::set to exclude multiple occurrences of two hex creatures*/
2010-12-25 21:23:30 +02:00
2012-05-18 23:50:16 +03:00
const ui8 attackerSide = sides [ 1 ] = = attackerOwner ;
const auto attackedHexes = s - > rangeInHexes ( destinationTile , skillLevel , attackerSide ) ;
2012-05-20 14:40:23 +03:00
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) ?
2012-02-17 00:19:07 +03:00
if ( s - > id = = Spells : : DEATH_RIPPLE | | s - > id = = Spells : : DESTROY_UNDEAD | | s - > id = = Spells : : ARMAGEDDON )
2010-12-25 21:23:30 +02:00
{
for ( int it = 0 ; it < stacks . size ( ) ; + + it )
{
2012-02-17 00:19:07 +03:00
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
2010-12-25 21:23:30 +02:00
)
{
2012-02-17 00:19:07 +03:00
if ( stacks [ it ] - > isValidTarget ( ) )
2010-12-25 21:23:30 +02:00
attackedCres . insert ( stacks [ it ] ) ;
}
}
}
2012-05-20 14:40:23 +03:00
else if ( s - > id = = Spells : : CHAIN_LIGHTNING )
{
std : : set < BattleHex > possibleHexes ;
BOOST_FOREACH ( auto stack , stacks )
{
if ( stack - > isValidTarget ( ) )
{
2012-05-25 14:49:56 +03:00
BOOST_FOREACH ( auto hex , stack - > getHexes ( ) )
{
possibleHexes . insert ( hex ) ;
}
2012-05-20 14:40:23 +03:00
}
}
BattleHex lightningHex = destinationTile ;
for ( int i = 0 ; i < 5 ; + + i ) //TODO: depends on spell school level
{
2012-08-26 12:07:48 +03:00
auto stack = battleGetStackByPos ( lightningHex , true ) ;
2012-05-20 14:40:23 +03:00
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 ) ;
}
}
2010-12-25 21:23:30 +02:00
else if ( s - > range [ skillLevel ] . size ( ) > 1 ) //custom many-hex range
{
2012-05-18 23:50:16 +03:00
BOOST_FOREACH ( BattleHex hex , attackedHexes )
2010-12-25 21:23:30 +02:00
{
2012-08-26 12:07:48 +03:00
if ( const CStack * st = battleGetStackByPos ( hex , onlyAlive ) )
2011-07-02 19:49:22 +03:00
{
if ( s - > id = = 76 ) //Death Cloud //TODO: fireball and fire immunity
{
2011-07-04 22:34:49 +03:00
if ( st - > isLiving ( ) | | st - > coversPos ( destinationTile ) ) //directly hit or alive
2011-07-02 19:49:22 +03:00
{
attackedCres . insert ( st ) ;
}
}
else
attackedCres . insert ( st ) ;
}
2010-12-25 21:23:30 +02:00
}
}
2011-02-21 18:53:23 +02:00
else if ( s - > getTargetType ( ) = = CSpell : : CREATURE_EXPERT_MASSIVE )
2010-12-25 21:23:30 +02:00
{
if ( skillLevel < 3 ) /*not expert */
{
2012-08-26 12:07:48 +03:00
const CStack * st = battleGetStackByPos ( destinationTile , onlyAlive ) ;
2010-12-25 21:23:30 +02:00
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 */
2012-02-17 00:19:07 +03:00
if ( ( ! s - > isNegative ( ) & & stacks [ it ] - > owner = = attackerOwner )
| | ( ! s - > isPositive ( ) & & stacks [ it ] - > owner ! = attackerOwner )
2010-12-25 21:23:30 +02:00
)
{
2012-02-17 00:19:07 +03:00
if ( stacks [ it ] - > isValidTarget ( ! onlyAlive ) )
2010-12-25 21:23:30 +02:00
attackedCres . insert ( stacks [ it ] ) ;
}
}
} //if(caster->getSpellSchoolLevel(s) < 3)
}
2011-02-21 18:53:23 +02:00
else if ( s - > getTargetType ( ) = = CSpell : : CREATURE )
2010-12-25 21:23:30 +02:00
{
2012-08-26 12:07:48 +03:00
if ( const CStack * st = battleGetStackByPos ( destinationTile , onlyAlive ) )
2010-12-25 21:23:30 +02:00
attackedCres . insert ( st ) ;
}
else //custom range from attackedHexes
{
2012-05-18 23:50:16 +03:00
BOOST_FOREACH ( BattleHex hex , attackedHexes )
2010-12-25 21:23:30 +02:00
{
2012-08-26 12:07:48 +03:00
if ( const CStack * st = battleGetStackByPos ( hex , onlyAlive ) )
2010-12-25 21:23:30 +02:00
attackedCres . insert ( st ) ;
}
}
return attackedCres ;
}
2011-10-17 11:24:51 +03:00
2011-07-08 17:54:20 +03:00
int BattleInfo : : calculateSpellDuration ( const CSpell * spell , const CGHeroInstance * caster , int usedSpellPower )
2010-12-25 21:23:30 +02:00
{
2011-07-08 17:54:20 +03:00
if ( ! caster )
{
if ( ! usedSpellPower )
return 3 ; //default duration of all creature spells
else
return usedSpellPower ; //use creature spell power
}
2010-12-25 21:23:30 +02:00
switch ( spell - > id )
{
2012-02-29 04:31:48 +03:00
case Spells : : FRENZY :
2010-12-25 21:23:30 +02:00
return 1 ;
default : //other spells
2012-02-29 04:31:48 +03:00
return caster - > getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) + caster - > valOfBonuses ( Bonus : : SPELL_DURATION ) ;
2010-12-25 21:23:30 +02:00
}
}
2012-02-29 04:31:48 +03:00
CStack * BattleInfo : : generateNewStack ( const CStackInstance & base , bool attackerOwned , int slot , BattleHex position ) const
2010-12-25 21:23:30 +02:00
{
2012-02-29 04:31:48 +03:00
int stackID = getIdForNewStack ( ) ;
2011-01-09 19:41:46 +02:00
int owner = attackerOwned ? sides [ 0 ] : sides [ 1 ] ;
2011-12-14 00:23:17 +03:00
assert ( ( owner > = GameConstants : : PLAYER_LIMIT ) | |
2011-05-30 22:20:14 +03:00
( base . armyObj & & base . armyObj - > tempOwner = = owner ) ) ;
2010-12-25 21:23:30 +02:00
CStack * ret = new CStack ( & base , owner , stackID , attackerOwned , slot ) ;
2012-06-16 17:25:39 +03:00
ret - > position = getAvaliableHex ( base . getCreatureID ( ) , attackerOwned , position ) ; //TODO: what if no free tile on battlefield was found?
2010-12-25 21:23:30 +02:00
return ret ;
}
2012-02-29 04:31:48 +03:00
CStack * BattleInfo : : generateNewStack ( const CStackBasicDescriptor & base , bool attackerOwned , int slot , BattleHex position ) const
2010-12-25 21:23:30 +02:00
{
2012-02-29 04:31:48 +03:00
int stackID = getIdForNewStack ( ) ;
2011-01-09 19:41:46 +02:00
int owner = attackerOwned ? sides [ 0 ] : sides [ 1 ] ;
2010-12-25 21:23:30 +02:00
CStack * ret = new CStack ( & base , owner , stackID , attackerOwned , slot ) ;
ret - > position = position ;
return ret ;
}
2012-08-26 12:07:48 +03:00
// std::pair<const CStack *, BattleHex> BattleInfo::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const
// {
// bool ac[GameConstants::BFIELD_SIZE];
// std::set<BattleHex> occupyable;
//
// getAccessibilityMap(ac, closest->doubleWide(), closest->attackerOwned, false, occupyable, closest->hasBonusOfType(Bonus::FLYING), closest);
//
// BattleHex predecessor[GameConstants::BFIELD_SIZE];
// int dist[GameConstants::BFIELD_SIZE];
// makeBFS(closest->position, ac, predecessor, dist, closest->doubleWide(), closest->attackerOwned, closest->hasBonusOfType(Bonus::FLYING), true);
//
// std::vector< std::pair< std::pair<int, int>, const CStack *> > stackPairs; //pairs <<distance, hex>, stack>
// for(int g=0; g<GameConstants::BFIELD_SIZE; ++g)
// {
// const CStack * atG = getStackT(g);
// if(!atG || atG->ID == closest->ID) //if there is not stack or we are the closest one
// continue;
// if(boost::logic::indeterminate(attackerOwned) || atG->attackerOwned == attackerOwned)
// {
// if(predecessor[g] == -1) //TODO: is it really the best solution?
// continue;
// stackPairs.push_back( std::make_pair( std::make_pair(dist[predecessor[g]], g), atG) );
// }
// }
//
// if(stackPairs.size() > 0)
// {
// std::vector< std::pair< std::pair<int, int>, const CStack *> > minimalPairs;
// minimalPairs.push_back(stackPairs[0]);
//
// for(int b=1; b<stackPairs.size(); ++b)
// {
// if(stackPairs[b].first.first < minimalPairs[0].first.first)
// {
// minimalPairs.clear();
// minimalPairs.push_back(stackPairs[b]);
// }
// else if(stackPairs[b].first.first == minimalPairs[0].first.first)
// {
// minimalPairs.push_back(stackPairs[b]);
// }
// }
//
// std::pair< std::pair<int, int>, const CStack *> minPair = minimalPairs[minimalPairs.size()/2];
//
// return std::make_pair(minPair.second, predecessor[minPair.first.second]);
// }
//
// return std::make_pair<const CStack * , BattleHex>(NULL, BattleHex::INVALID);
// }
2010-12-25 21:23:30 +02:00
ui32 BattleInfo : : calculateSpellBonus ( ui32 baseDamage , const CSpell * sp , const CGHeroInstance * caster , const CStack * affectedCreature ) const
{
ui32 ret = baseDamage ;
//applying sorcery secondary skill
if ( caster )
{
2011-12-14 00:23:17 +03:00
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 ;
2010-12-25 21:23:30 +02:00
if ( sp - > air )
2011-12-14 00:23:17 +03:00
ret * = ( 100.0 + caster - > valOfBonuses ( Bonus : : AIR_SPELL_DMG_PREMY ) ) / 100.0 ;
2010-12-25 21:23:30 +02:00
else if ( sp - > fire ) //only one type of bonus for Magic Arrow
2011-12-14 00:23:17 +03:00
ret * = ( 100.0 + caster - > valOfBonuses ( Bonus : : FIRE_SPELL_DMG_PREMY ) ) / 100.0 ;
2010-12-25 21:23:30 +02:00
else if ( sp - > water )
2011-12-14 00:23:17 +03:00
ret * = ( 100.0 + caster - > valOfBonuses ( Bonus : : WATER_SPELL_DMG_PREMY ) ) / 100.0 ;
2010-12-25 21:23:30 +02:00
else if ( sp - > earth )
2011-12-14 00:23:17 +03:00
ret * = ( 100.0 + caster - > valOfBonuses ( Bonus : : EARTH_SPELL_DMG_PREMY ) ) / 100.0 ;
2010-12-25 21:23:30 +02:00
2011-02-11 14:27:38 +02:00
if ( affectedCreature & & affectedCreature - > getCreature ( ) - > level ) //Hero specials like Solmyr, Deemer
2011-12-14 00:23:17 +03:00
ret * = ( 100. + ( ( caster - > valOfBonuses ( Bonus : : SPECIAL_SPELL_LEV , sp - > id ) * caster - > level ) / affectedCreature - > getCreature ( ) - > level ) ) / 100.0 ;
2010-12-25 21:23:30 +02:00
}
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
2011-06-25 09:55:35 +03:00
if ( VLC - > spellh - > damageSpells . find ( sp - > id ) = = VLC - > spellh - > damageSpells . end ( ) )
2010-12-25 21:23:30 +02:00
return 0 ;
2011-05-29 16:34:26 +03:00
ret = usedSpellPower * sp - > power ;
2010-12-25 21:23:30 +02:00
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 ;
}
2012-04-28 22:40:27 +03:00
ui32 BattleInfo : : calculateHealedHP ( const CGHeroInstance * caster , const CSpell * spell , const CStack * stack , const CStack * sacrificedStack ) const
2010-12-25 21:23:30 +02:00
{
2011-07-08 16:17:05 +03:00
bool resurrect = resurrects ( spell - > id ) ;
2012-04-28 22:40:27 +03:00
int healedHealth ;
if ( spell - > id = = Spells : : SACRIFICE & & sacrificedStack )
healedHealth = ( caster - > getPrimSkillLevel ( 2 ) + sacrificedStack - > MaxHealth ( ) + spell - > powers [ caster - > getSpellSchoolLevel ( spell ) ] ) * sacrificedStack - > count ;
else
healedHealth = caster - > getPrimSkillLevel ( 2 ) * spell - > power + spell - > powers [ caster - > getSpellSchoolLevel ( spell ) ] ;
2012-08-26 12:07:48 +03:00
healedHealth = calculateSpellBonus ( healedHealth , spell , caster , stack ) ;
return std : : min < ui32 > ( healedHealth , stack - > MaxHealth ( ) - stack - > firstHPleft + ( resurrect ? stack - > baseAmount * stack - > MaxHealth ( ) : 0 ) ) ;
2010-12-25 21:23:30 +02:00
}
2012-08-26 12:07:48 +03:00
ui32 BattleInfo : : calculateHealedHP ( int healedHealth , const CSpell * spell , const CStack * stack ) const
2011-01-07 12:48:31 +02:00
{
2012-08-26 12:07:48 +03:00
bool resurrect = resurrects ( spell - > id ) ;
return std : : min < ui32 > ( healedHealth , stack - > MaxHealth ( ) - stack - > firstHPleft + ( resurrect ? stack - > baseAmount * stack - > MaxHealth ( ) : 0 ) ) ;
2011-01-07 12:48:31 +02:00
}
2012-08-26 12:07:48 +03:00
ui32 BattleInfo : : calculateHealedHP ( const CSpell * spell , int usedSpellPower , int spellSchoolLevel , const CStack * stack ) const
2010-12-25 21:23:30 +02:00
{
2012-08-26 12:07:48 +03:00
bool resurrect = resurrects ( spell - > id ) ;
int healedHealth = usedSpellPower * spell - > power + spell - > powers [ spellSchoolLevel ] ;
return std : : min < ui32 > ( healedHealth , stack - > MaxHealth ( ) - stack - > firstHPleft + ( resurrect ? stack - > baseAmount * stack - > MaxHealth ( ) : 0 ) ) ;
}
bool BattleInfo : : resurrects ( TSpell spellid ) const
{
return vstd : : contains ( VLC - > spellh - > risingSpells , spellid ) ;
2011-01-07 12:48:31 +02:00
}
2011-12-22 16:05:19 +03:00
const CStack * BattleInfo : : battleGetStack ( BattleHex pos , bool onlyAlive )
2011-01-07 12:48:31 +02:00
{
2011-11-06 20:39:49 +03:00
CStack * stack = NULL ;
2011-12-14 00:23:17 +03:00
for ( ui32 g = 0 ; g < stacks . size ( ) ; + + g )
2011-01-07 12:48:31 +02:00
{
2011-11-06 20:39:49 +03:00
if ( stacks [ g ] - > position = = pos
2011-01-07 12:48:31 +02:00
| | ( stacks [ g ] - > doubleWide ( )
& & ( ( stacks [ g ] - > attackerOwned & & stacks [ g ] - > position - 1 = = pos )
| | ( ! stacks [ g ] - > attackerOwned & & stacks [ g ] - > position + 1 = = pos ) )
2011-11-06 20:39:49 +03:00
) )
{
if ( stacks [ g ] - > alive ( ) )
return stacks [ g ] ; //we prefer living stacks - there cna be only one stack on te tile, so return it imediately
else if ( ! onlyAlive )
stack = stacks [ g ] ; //dead stacks are only accessible when there's no alive stack on this tile
}
2011-01-07 12:48:31 +02:00
}
2011-11-06 20:39:49 +03:00
return stack ;
2011-01-07 12:48:31 +02:00
}
2011-01-08 20:33:40 +02:00
const CGHeroInstance * BattleInfo : : battleGetOwner ( const CStack * stack ) const
2011-01-07 12:48:31 +02:00
{
return heroes [ ! stack - > attackerOwned ] ;
}
2010-12-25 21:23:30 +02:00
void BattleInfo : : localInit ( )
{
belligerents [ 0 ] - > battle = belligerents [ 1 ] - > battle = this ;
2011-02-21 06:13:00 +02:00
BOOST_FOREACH ( CArmedInstance * b , belligerents )
b - > attachTo ( this ) ;
2010-12-25 21:23:30 +02:00
BOOST_FOREACH ( CStack * s , stacks )
2012-02-29 04:31:48 +03:00
localInitStack ( s ) ;
2011-03-01 12:19:05 +02:00
exportBonuses ( ) ;
2010-12-25 21:23:30 +02:00
}
2012-02-29 04:31:48 +03:00
void BattleInfo : : localInitStack ( CStack * s )
{
s - > exportBonuses ( ) ;
if ( s - > base ) //stack originating from "real" stack in garrison -> attach to it
{
s - > attachTo ( const_cast < CStackInstance * > ( s - > base ) ) ;
}
else //attach directly to obj to which stack belongs and creature type
{
CArmedInstance * army = belligerents [ ! s - > attackerOwned ] ;
s - > attachTo ( army ) ;
assert ( s - > type ) ;
s - > attachTo ( const_cast < CCreature * > ( s - > type ) ) ;
}
s - > postInit ( ) ;
}
2010-12-25 21:23:30 +02:00
namespace CGH
{
using namespace std ;
2011-09-03 22:25:37 +03:00
static void readBattlePositions ( const JsonNode & node , vector < vector < int > > & dest )
2010-12-25 21:23:30 +02:00
{
2011-09-03 22:25:37 +03:00
BOOST_FOREACH ( const JsonNode & level , node . Vector ( ) )
2010-12-25 21:23:30 +02:00
{
std : : vector < int > pom ;
2011-09-03 22:25:37 +03:00
BOOST_FOREACH ( const JsonNode & value , level . Vector ( ) )
2010-12-25 21:23:30 +02:00
{
2011-09-03 22:25:37 +03:00
pom . push_back ( value . Float ( ) ) ;
2010-12-25 21:23:30 +02:00
}
2011-09-03 22:25:37 +03:00
2010-12-25 21:23:30 +02:00
dest . push_back ( pom ) ;
}
}
}
2012-04-23 22:56:37 +03:00
//RNG that works like H3 one
struct RandGen
{
int seed ;
void srand ( int s )
{
seed = s ;
}
void srand ( int3 pos )
{
srand ( 110291 * pos . x + 167801 * pos . y + 81569 ) ;
}
int rand ( )
{
seed = 214013 * seed + 2531011 ;
return ( seed > > 16 ) & 0x7FFF ;
}
int rand ( int min , int max )
{
if ( min = = max )
return min ;
if ( min > max )
return min ;
return min + rand ( ) % ( max - min + 1 ) ;
}
} ;
struct RangeGenerator
{
class ExhaustedPossibilities : public std : : exception
{
} ;
RangeGenerator ( int _min , int _max , boost : : function < int ( ) > _myRand )
{
myRand = _myRand ;
min = _min ;
remainingCount = _max - _min + 1 ;
remaining . resize ( remainingCount , true ) ;
}
2012-04-24 08:16:04 +03:00
int generateNumber ( )
{
if ( ! remainingCount )
throw ExhaustedPossibilities ( ) ;
if ( remainingCount = = 1 )
return 0 ;
return myRand ( ) % remainingCount ;
}
2012-04-23 22:56:37 +03:00
//get number fulfilling predicate. Never gives the same number twice.
int getSuchNumber ( boost : : function < bool ( int ) > goodNumberPred = 0 )
{
int ret = - 1 ;
do
{
2012-04-24 08:16:04 +03:00
int n = generateNumber ( ) ;
2012-04-23 22:56:37 +03:00
int i = 0 ;
for ( ; ; i + + )
{
assert ( i < ( int ) remaining . size ( ) ) ;
if ( ! remaining [ i ] )
continue ;
if ( ! n )
break ;
n - - ;
}
remainingCount - - ;
remaining [ i ] = false ;
ret = i + min ;
} while ( goodNumberPred & & ! goodNumberPred ( ret ) ) ;
return ret ;
}
int min , remainingCount ;
std : : vector < bool > remaining ;
boost : : function < int ( ) > myRand ;
} ;
2012-05-18 23:50:16 +03:00
BattleInfo * BattleInfo : : setupBattle ( int3 tile , int terrain , int battlefieldType , const CArmedInstance * armies [ 2 ] , const CGHeroInstance * heroes [ 2 ] , bool creatureBank , const CGTownInstance * town )
2010-12-25 21:23:30 +02:00
{
CMP_stack cmpst ;
BattleInfo * curB = new BattleInfo ;
2012-03-29 00:54:43 +03:00
curB - > castSpells [ 0 ] = curB - > castSpells [ 1 ] = 0 ;
2011-01-09 19:41:46 +02:00
curB - > sides [ 0 ] = armies [ 0 ] - > tempOwner ;
curB - > sides [ 1 ] = armies [ 1 ] - > tempOwner ;
if ( curB - > sides [ 1 ] = = 254 )
curB - > sides [ 1 ] = 255 ;
2010-12-25 21:23:30 +02:00
std : : vector < CStack * > & stacks = ( curB - > stacks ) ;
curB - > tile = tile ;
2012-05-18 23:50:16 +03:00
curB - > battlefieldType = battlefieldType ;
2010-12-25 21:23:30 +02:00
curB - > belligerents [ 0 ] = const_cast < CArmedInstance * > ( armies [ 0 ] ) ;
curB - > belligerents [ 1 ] = const_cast < CArmedInstance * > ( armies [ 1 ] ) ;
curB - > heroes [ 0 ] = const_cast < CGHeroInstance * > ( heroes [ 0 ] ) ;
curB - > heroes [ 1 ] = const_cast < CGHeroInstance * > ( heroes [ 1 ] ) ;
curB - > round = - 2 ;
curB - > activeStack = - 1 ;
2011-10-09 10:20:23 +03:00
curB - > enchanterCounter [ 0 ] = curB - > enchanterCounter [ 1 ] = 0 ; //ready to cast
2010-12-25 21:23:30 +02:00
if ( town )
{
2011-01-07 12:48:31 +02:00
curB - > town = town ;
2010-12-25 21:23:30 +02:00
curB - > siege = town - > fortLevel ( ) ;
2012-05-18 23:50:16 +03:00
curB - > terrainType = VLC - > heroh - > nativeTerrains [ town - > town - > typeID ] ;
2010-12-25 21:23:30 +02:00
}
else
{
2011-01-07 12:48:31 +02:00
curB - > town = NULL ;
2010-12-25 21:23:30 +02:00
curB - > siege = 0 ;
2012-05-18 23:50:16 +03:00
curB - > terrainType = terrain ;
2010-12-25 21:23:30 +02:00
}
2012-06-16 17:25:39 +03:00
//setting up siege obstacles
if ( town & & town - > hasFort ( ) )
{
for ( int b = 0 ; b < ARRAY_COUNT ( curB - > si . wallState ) ; + + b )
{
curB - > si . wallState [ b ] = 1 ;
}
}
//randomize obstacles
if ( town = = NULL & & ! creatureBank ) //do it only when it's not siege and not creature bank
{
const int ABSOLUTE_OBSTACLES_COUNT = 34 , USUAL_OBSTACLES_COUNT = 91 ; //shouldn't be changes if we want H3-like obstacle placement
RandGen r ;
auto ourRand = [ & ] { return r . rand ( ) ; } ;
r . srand ( tile ) ;
r . rand ( 1 , 8 ) ; //battle sound ID to play... can't do anything with it here
int tilesToBlock = r . rand ( 5 , 12 ) ;
const int specialBattlefield = battlefieldTypeToBI ( battlefieldType ) ;
std : : vector < BattleHex > blockedTiles ;
auto appropriateAbsoluteObstacle = [ & ] ( int id )
{
return VLC - > heroh - > absoluteObstacles [ id ] . isAppropriate ( curB - > terrainType , specialBattlefield ) ;
} ;
auto appropriateUsualObstacle = [ & ] ( int id ) - > bool
{
return VLC - > heroh - > obstacles [ id ] . isAppropriate ( curB - > terrainType , specialBattlefield ) ;
} ;
if ( r . rand ( 1 , 100 ) < = 40 ) //put cliff-like obstacle
{
RangeGenerator obidgen ( 0 , ABSOLUTE_OBSTACLES_COUNT - 1 , ourRand ) ;
try
{
auto obstPtr = make_shared < CObstacleInstance > ( ) ;
obstPtr - > obstacleType = CObstacleInstance : : ABSOLUTE_OBSTACLE ;
obstPtr - > ID = obidgen . getSuchNumber ( appropriateAbsoluteObstacle ) ;
obstPtr - > uniqueID = curB - > obstacles . size ( ) ;
curB - > obstacles . push_back ( obstPtr ) ;
BOOST_FOREACH ( BattleHex blocked , obstPtr - > getBlockedTiles ( ) )
blockedTiles . push_back ( blocked ) ;
tilesToBlock - = VLC - > heroh - > absoluteObstacles [ obstPtr - > ID ] . blockedTiles . size ( ) / 2 ;
}
catch ( RangeGenerator : : ExhaustedPossibilities & )
{
//silently ignore, if we can't place absolute obstacle, we'll go with the usual ones
}
}
RangeGenerator obidgen ( 0 , USUAL_OBSTACLES_COUNT - 1 , ourRand ) ;
try
{
while ( tilesToBlock > 0 )
{
const int obid = obidgen . getSuchNumber ( appropriateUsualObstacle ) ;
const CObstacleInfo & obi = VLC - > heroh - > obstacles [ obid ] ;
auto validPosition = [ & ] ( BattleHex pos ) - > bool
{
if ( obi . height > = pos . getY ( ) )
return false ;
if ( pos . getX ( ) = = 0 )
return false ;
if ( pos . getX ( ) + obi . width > 15 )
return false ;
if ( vstd : : contains ( blockedTiles , pos ) )
return false ;
BOOST_FOREACH ( BattleHex blocked , obi . getBlocked ( pos ) )
{
if ( vstd : : contains ( blockedTiles , blocked ) )
return false ;
int x = blocked . getX ( ) ;
if ( x < = 2 | | x > = 14 )
return false ;
}
return true ;
} ;
RangeGenerator posgenerator ( 18 , 168 , ourRand ) ;
auto obstPtr = make_shared < CObstacleInstance > ( ) ;
obstPtr - > ID = obid ;
obstPtr - > pos = posgenerator . getSuchNumber ( validPosition ) ;
obstPtr - > uniqueID = curB - > obstacles . size ( ) ;
curB - > obstacles . push_back ( obstPtr ) ;
BOOST_FOREACH ( BattleHex blocked , obstPtr - > getBlockedTiles ( ) )
blockedTiles . push_back ( blocked ) ;
tilesToBlock - = obi . blockedTiles . size ( ) ;
}
}
catch ( RangeGenerator : : ExhaustedPossibilities & )
{
}
}
//reading battleStartpos - add creatures AFTER random obstacles are generated
2012-04-28 14:41:20 +03:00
//TODO: parse once to some structure
2010-12-25 21:23:30 +02:00
std : : vector < std : : vector < int > > attackerLoose , defenderLoose , attackerTight , defenderTight , attackerCreBank , defenderCreBank ;
2012-04-28 14:41:20 +03:00
std : : vector < int > commanderField , commanderBank ;
2012-08-02 14:03:26 +03:00
const JsonNode config ( ResourceID ( " config/battleStartpos.json " ) ) ;
2011-09-03 22:25:37 +03:00
const JsonVector & positions = config [ " battle_positions " ] . Vector ( ) ;
CGH : : readBattlePositions ( positions [ 0 ] [ " levels " ] , attackerLoose ) ;
CGH : : readBattlePositions ( positions [ 1 ] [ " levels " ] , defenderLoose ) ;
CGH : : readBattlePositions ( positions [ 2 ] [ " levels " ] , attackerTight ) ;
CGH : : readBattlePositions ( positions [ 3 ] [ " levels " ] , defenderTight ) ;
CGH : : readBattlePositions ( positions [ 4 ] [ " levels " ] , attackerCreBank ) ;
CGH : : readBattlePositions ( positions [ 5 ] [ " levels " ] , defenderCreBank ) ;
2012-04-28 14:41:20 +03:00
BOOST_FOREACH ( auto position , config [ " commanderPositions " ] [ " field " ] . Vector ( ) )
{
commanderField . push_back ( position . Float ( ) ) ;
}
BOOST_FOREACH ( auto position , config [ " commanderPositions " ] [ " creBank " ] . Vector ( ) )
{
commanderBank . push_back ( position . Float ( ) ) ;
}
2010-12-25 21:23:30 +02:00
2011-09-03 22:25:37 +03:00
//battleStartpos read
2010-12-25 21:23:30 +02:00
int k = 0 ; //stack serial
for ( TSlots : : const_iterator i = armies [ 0 ] - > Slots ( ) . begin ( ) ; i ! = armies [ 0 ] - > Slots ( ) . end ( ) ; i + + , k + + )
{
int pos ;
if ( creatureBank )
pos = attackerCreBank [ armies [ 0 ] - > stacksCount ( ) - 1 ] [ k ] ;
else if ( armies [ 0 ] - > formation )
pos = attackerTight [ armies [ 0 ] - > stacksCount ( ) - 1 ] [ k ] ;
else
pos = attackerLoose [ armies [ 0 ] - > stacksCount ( ) - 1 ] [ k ] ;
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( * i - > second , true , i - > first , pos ) ;
2010-12-25 21:23:30 +02:00
stacks . push_back ( stack ) ;
}
k = 0 ;
for ( TSlots : : const_iterator i = armies [ 1 ] - > Slots ( ) . begin ( ) ; i ! = armies [ 1 ] - > Slots ( ) . end ( ) ; i + + , k + + )
{
int pos ;
if ( creatureBank )
pos = defenderCreBank [ armies [ 1 ] - > stacksCount ( ) - 1 ] [ k ] ;
else if ( armies [ 1 ] - > formation )
pos = defenderTight [ armies [ 1 ] - > stacksCount ( ) - 1 ] [ k ] ;
else
pos = defenderLoose [ armies [ 1 ] - > stacksCount ( ) - 1 ] [ k ] ;
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( * i - > second , false , i - > first , pos ) ;
2010-12-25 21:23:30 +02:00
stacks . push_back ( stack ) ;
}
2012-04-23 22:56:37 +03:00
//shifting positions of two-hex creatures
for ( unsigned g = 0 ; g < stacks . size ( ) ; + + g )
2010-12-25 21:23:30 +02:00
{
2011-01-18 20:54:53 +02:00
//we should do that for creature bank too
if ( stacks [ g ] - > doubleWide ( ) & & stacks [ g ] - > attackerOwned )
2010-12-25 21:23:30 +02:00
{
2011-12-22 16:05:19 +03:00
stacks [ g ] - > position + = BattleHex : : RIGHT ;
2010-12-25 21:23:30 +02:00
}
2011-01-18 20:54:53 +02:00
else if ( stacks [ g ] - > doubleWide ( ) & & ! stacks [ g ] - > attackerOwned )
2010-12-25 21:23:30 +02:00
{
2011-10-16 23:19:18 +03:00
if ( stacks [ g ] - > position . getX ( ) > 1 )
2011-12-22 16:05:19 +03:00
stacks [ g ] - > position + = BattleHex : : LEFT ;
2010-12-25 21:23:30 +02:00
}
}
//adding war machines
2011-03-01 12:28:31 +02:00
if ( ! creatureBank )
2010-12-25 21:23:30 +02:00
{
2011-03-01 12:28:31 +02:00
if ( heroes [ 0 ] )
2010-12-25 21:23:30 +02:00
{
2011-03-01 12:28:31 +02:00
if ( heroes [ 0 ] - > getArt ( 13 ) ) //ballista
{
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 146 , 1 ) , true , 255 , 52 ) ;
2011-03-01 12:28:31 +02:00
stacks . push_back ( stack ) ;
}
if ( heroes [ 0 ] - > getArt ( 14 ) ) //ammo cart
{
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 148 , 1 ) , true , 255 , 18 ) ;
2011-03-01 12:28:31 +02:00
stacks . push_back ( stack ) ;
}
if ( heroes [ 0 ] - > getArt ( 15 ) ) //first aid tent
{
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 147 , 1 ) , true , 255 , 154 ) ;
2011-03-01 12:28:31 +02:00
stacks . push_back ( stack ) ;
}
2010-12-25 21:23:30 +02:00
}
2011-03-01 12:28:31 +02:00
if ( heroes [ 1 ] )
2010-12-25 21:23:30 +02:00
{
2011-03-01 12:28:31 +02:00
//defending hero shouldn't receive ballista (bug #551)
if ( heroes [ 1 ] - > getArt ( 13 ) & & ! town ) //ballista
{
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 146 , 1 ) , false , 255 , 66 ) ;
2011-03-01 12:28:31 +02:00
stacks . push_back ( stack ) ;
}
if ( heroes [ 1 ] - > getArt ( 14 ) ) //ammo cart
{
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 148 , 1 ) , false , 255 , 32 ) ;
2011-03-01 12:28:31 +02:00
stacks . push_back ( stack ) ;
}
if ( heroes [ 1 ] - > getArt ( 15 ) ) //first aid tent
{
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 147 , 1 ) , false , 255 , 168 ) ;
2011-03-01 12:28:31 +02:00
stacks . push_back ( stack ) ;
}
2010-12-25 21:23:30 +02:00
}
2011-03-01 12:28:31 +02:00
if ( town & & heroes [ 0 ] & & town - > hasFort ( ) ) //catapult
2010-12-25 21:23:30 +02:00
{
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 145 , 1 ) , true , 255 , 120 ) ;
2010-12-25 21:23:30 +02:00
stacks . push_back ( stack ) ;
}
}
//war machines added
2012-04-28 14:41:20 +03:00
//adding commanders
for ( int i = 0 ; i < 2 ; + + i )
{
if ( heroes [ i ] & & heroes [ i ] - > commander )
{
2012-08-20 19:10:50 +03:00
CStack * stack = curB - > generateNewStack ( * heroes [ i ] - > commander , ! i , - 2 , //TODO: use COMMANDER_SLOT_PLACEHOLDER
2012-04-28 14:41:20 +03:00
creatureBank ? commanderBank [ i ] : commanderField [ i ] ) ;
stacks . push_back ( stack ) ;
}
}
2011-05-18 20:51:10 +03:00
if ( curB - > siege = = 2 | | curB - > siege = = 3 )
2010-12-25 21:23:30 +02:00
{
2011-05-18 20:51:10 +03:00
// keep tower
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 149 , 1 ) , false , 255 , - 2 ) ;
2011-05-18 20:51:10 +03:00
stacks . push_back ( stack ) ;
2010-12-25 21:23:30 +02:00
2011-05-18 20:51:10 +03:00
if ( curB - > siege = = 3 )
{
// lower tower + upper tower
2012-02-29 04:31:48 +03:00
CStack * stack = curB - > generateNewStack ( CStackBasicDescriptor ( 149 , 1 ) , false , 255 , - 4 ) ;
2010-12-25 21:23:30 +02:00
stacks . push_back ( stack ) ;
2012-02-29 04:31:48 +03:00
stack = curB - > generateNewStack ( CStackBasicDescriptor ( 149 , 1 ) , false , 255 , - 3 ) ;
2010-12-25 21:23:30 +02:00
stacks . push_back ( stack ) ;
}
2012-05-18 23:50:16 +03:00
//moat
auto moat = make_shared < MoatObstacle > ( ) ;
moat - > ID = curB - > town - > subID ;
moat - > obstacleType = CObstacleInstance : : MOAT ;
moat - > uniqueID = curB - > obstacles . size ( ) ;
curB - > obstacles . push_back ( moat ) ;
2010-12-25 21:23:30 +02:00
}
std : : stable_sort ( stacks . begin ( ) , stacks . end ( ) , cmpst ) ;
2011-02-21 15:27:31 +02:00
//spell level limiting bonus
2011-02-21 18:53:23 +02:00
curB - > addNewBonus ( new Bonus ( Bonus : : ONE_BATTLE , Bonus : : LEVEL_SPELL_IMMUNITY , Bonus : : OTHER ,
0 , - 1 , - 1 , Bonus : : INDEPENDENT_MAX ) ) ;
2011-02-21 15:27:31 +02:00
2010-12-25 21:23:30 +02:00
//giving terrain overalay premies
int bonusSubtype = - 1 ;
2012-05-18 23:50:16 +03:00
switch ( battlefieldType )
2010-12-25 21:23:30 +02:00
{
case 9 : //magic plains
{
bonusSubtype = 0 ;
}
case 14 : //fiery fields
{
if ( bonusSubtype = = - 1 ) bonusSubtype = 1 ;
}
case 15 : //rock lands
{
if ( bonusSubtype = = - 1 ) bonusSubtype = 8 ;
}
case 16 : //magic clouds
{
if ( bonusSubtype = = - 1 ) bonusSubtype = 2 ;
}
case 17 : //lucid pools
{
if ( bonusSubtype = = - 1 ) bonusSubtype = 4 ;
}
{ //common part for cases 9, 14, 15, 16, 17
curB - > addNewBonus ( new Bonus ( Bonus : : ONE_BATTLE , Bonus : : MAGIC_SCHOOL_SKILL , Bonus : : TERRAIN_OVERLAY , 3 , - 1 , " " , bonusSubtype ) ) ;
break ;
}
case 18 : //holy ground
{
2012-03-06 19:59:55 +03:00
curB - > addNewBonus ( makeFeature ( Bonus : : MORALE , Bonus : : ONE_BATTLE , 0 , + 1 , Bonus : : TERRAIN_OVERLAY ) - > addLimiter ( make_shared < CreatureAlignmentLimiter > ( EAlignment : : GOOD ) ) ) ;
curB - > addNewBonus ( makeFeature ( Bonus : : MORALE , Bonus : : ONE_BATTLE , 0 , - 1 , Bonus : : TERRAIN_OVERLAY ) - > addLimiter ( make_shared < CreatureAlignmentLimiter > ( EAlignment : : EVIL ) ) ) ;
2010-12-25 21:23:30 +02:00
break ;
}
case 19 : //clover field
{ //+2 luck bonus for neutral creatures
2012-03-06 19:59:55 +03:00
curB - > addNewBonus ( makeFeature ( Bonus : : LUCK , Bonus : : ONE_BATTLE , 0 , + 2 , Bonus : : TERRAIN_OVERLAY ) - > addLimiter ( make_shared < CreatureFactionLimiter > ( - 1 ) ) ) ;
2010-12-25 21:23:30 +02:00
break ;
}
case 20 : //evil fog
{
2012-03-06 19:59:55 +03:00
curB - > addNewBonus ( makeFeature ( Bonus : : MORALE , Bonus : : ONE_BATTLE , 0 , - 1 , Bonus : : TERRAIN_OVERLAY ) - > addLimiter ( make_shared < CreatureAlignmentLimiter > ( EAlignment : : GOOD ) ) ) ;
curB - > addNewBonus ( makeFeature ( Bonus : : MORALE , Bonus : : ONE_BATTLE , 0 , + 1 , Bonus : : TERRAIN_OVERLAY ) - > addLimiter ( make_shared < CreatureAlignmentLimiter > ( EAlignment : : EVIL ) ) ) ;
2010-12-25 21:23:30 +02:00
break ;
}
case 22 : //cursed ground
{
curB - > addNewBonus ( makeFeature ( Bonus : : NO_MORALE , Bonus : : ONE_BATTLE , 0 , 0 , Bonus : : TERRAIN_OVERLAY ) ) ;
curB - > addNewBonus ( makeFeature ( Bonus : : NO_LUCK , Bonus : : ONE_BATTLE , 0 , 0 , Bonus : : TERRAIN_OVERLAY ) ) ;
2011-12-14 00:23:17 +03:00
Bonus * b = makeFeature ( Bonus : : LEVEL_SPELL_IMMUNITY , Bonus : : ONE_BATTLE , GameConstants : : SPELL_LEVELS , 1 , Bonus : : TERRAIN_OVERLAY ) ;
2011-02-21 18:53:23 +02:00
b - > valType = Bonus : : INDEPENDENT_MAX ;
2011-02-21 15:27:31 +02:00
curB - > addNewBonus ( b ) ;
2010-12-25 21:23:30 +02:00
break ;
}
}
//overlay premies given
//native terrain bonuses
2012-05-18 23:50:16 +03:00
auto nativeTerrain = make_shared < CreatureNativeTerrainLimiter > ( curB - > terrainType ) ;
2010-12-25 21:23:30 +02:00
curB - > addNewBonus ( makeFeature ( Bonus : : STACKS_SPEED , Bonus : : ONE_BATTLE , 0 , 1 , Bonus : : TERRAIN_NATIVE ) - > addLimiter ( nativeTerrain ) ) ;
curB - > addNewBonus ( makeFeature ( Bonus : : PRIMARY_SKILL , Bonus : : ONE_BATTLE , PrimarySkill : : ATTACK , 1 , Bonus : : TERRAIN_NATIVE ) - > addLimiter ( nativeTerrain ) ) ;
curB - > addNewBonus ( makeFeature ( Bonus : : PRIMARY_SKILL , Bonus : : ONE_BATTLE , PrimarySkill : : DEFENSE , 1 , Bonus : : TERRAIN_NATIVE ) - > addLimiter ( nativeTerrain ) ) ;
//////////////////////////////////////////////////////////////////////////
2011-02-21 06:13:00 +02:00
//tactics
2012-03-29 00:54:43 +03:00
bool isTacticsAllowed = ! creatureBank ; //no tactics in crebanks
2011-02-12 18:12:48 +02:00
int tacticLvls [ 2 ] = { 0 } ;
for ( int i = 0 ; i < ARRAY_COUNT ( tacticLvls ) ; i + + )
{
if ( heroes [ i ] )
tacticLvls [ i ] + = heroes [ i ] - > getSecSkillLevel ( CGHeroInstance : : TACTICS ) ;
}
2012-03-29 00:54:43 +03:00
int tacticsSkillDiff = tacticLvls [ 0 ] - tacticLvls [ 1 ] ;
2010-12-25 21:23:30 +02:00
2012-03-29 00:54:43 +03:00
if ( tacticsSkillDiff & & isTacticsAllowed )
2011-02-12 18:12:48 +02:00
{
2012-03-29 00:54:43 +03:00
curB - > tacticsSide = tacticsSkillDiff < 0 ;
curB - > tacticDistance = std : : abs ( tacticsSkillDiff ) * 2 + 1 ;
2011-02-12 18:12:48 +02:00
}
2011-02-14 00:46:04 +02:00
else
curB - > tacticDistance = 0 ;
2010-12-25 21:23:30 +02:00
2011-02-21 06:13:00 +02:00
2011-02-28 17:14:26 +02:00
// workaround bonuses affecting only enemy
2011-02-21 06:13:00 +02:00
for ( int i = 0 ; i < 2 ; i + + )
{
TNodes nodes ;
curB - > belligerents [ i ] - > getRedAncestors ( nodes ) ;
BOOST_FOREACH ( CBonusSystemNode * n , nodes )
{
2011-07-13 21:39:02 +03:00
BOOST_FOREACH ( Bonus * b , n - > getExportedBonusList ( ) )
2011-02-21 06:13:00 +02:00
{
if ( b - > effectRange = = Bonus : : ONLY_ENEMY_ARMY /* && b->propagator && b->propagator->shouldBeAttached(curB)*/ )
{
Bonus * bCopy = new Bonus ( * b ) ;
bCopy - > effectRange = Bonus : : NO_LIMIT ;
bCopy - > propagator . reset ( ) ;
bCopy - > limiter . reset ( new StackOwnerLimiter ( curB - > sides [ ! i ] ) ) ;
curB - > addNewBonus ( bCopy ) ;
}
}
}
}
2011-02-12 18:12:48 +02:00
return curB ;
}
2010-12-25 21:23:30 +02:00
2011-02-21 18:53:23 +02:00
const CGHeroInstance * BattleInfo : : getHero ( int player ) const
{
2011-03-01 15:54:48 +02:00
assert ( sides [ 0 ] = = player | | sides [ 1 ] = = player ) ;
2011-02-21 18:53:23 +02:00
if ( heroes [ 0 ] & & heroes [ 0 ] - > getOwner ( ) = = player )
return heroes [ 0 ] ;
return heroes [ 1 ] ;
}
2012-08-26 12:07:48 +03:00
std : : vector < ui32 > BattleInfo : : calculateResistedStacks ( const CSpell * sp , const CGHeroInstance * caster , const CGHeroInstance * hero2 , const std : : set < const CStack * > affectedCreatures , int casterSideOwner , ECastingMode : : ECastingMode mode , int usedSpellPower , int spellLevel ) const
2011-02-22 15:44:34 +02:00
{
std : : vector < ui32 > ret ;
2012-08-26 12:07:48 +03:00
for ( auto it = affectedCreatures . begin ( ) ; it ! = affectedCreatures . end ( ) ; + + it )
2011-02-22 15:44:34 +02:00
{
2011-12-14 00:23:17 +03:00
if ( battleIsImmune ( caster , sp , mode , ( * it ) - > position ) ! = ESpellCastProblem : : OK )
2011-02-22 15:44:34 +02:00
{
ret . push_back ( ( * it ) - > ID ) ;
continue ;
}
//non-negative spells on friendly stacks should always succeed, unless immune
2012-02-17 00:19:07 +03:00
if ( ! sp - > isNegative ( ) & & ( * it ) - > owner = = casterSideOwner )
2011-02-22 15:44:34 +02:00
continue ;
2011-11-25 22:41:23 +03:00
/*
2011-02-22 15:44:34 +02:00
const CGHeroInstance * bonusHero ; //hero we should take bonuses from
if ( ( * it ) - > owner = = casterSideOwner )
bonusHero = caster ;
else
2011-11-25 22:41:23 +03:00
bonusHero = hero2 ; */
2011-02-22 15:44:34 +02:00
2011-05-29 21:28:50 +03:00
int prob = ( * it ) - > magicResistance ( ) ; //probability of resistance in %
2011-02-22 15:44:34 +02:00
if ( prob > 100 ) prob = 100 ;
if ( rand ( ) % 100 < prob ) //immunity from resistance
ret . push_back ( ( * it ) - > ID ) ;
}
if ( sp - > id = = 60 ) //hypnotize
{
2012-08-26 12:07:48 +03:00
for ( auto it = affectedCreatures . begin ( ) ; it ! = affectedCreatures . end ( ) ; + + it )
2011-02-22 15:44:34 +02:00
{
if ( ( * it ) - > hasBonusOfType ( Bonus : : SPELL_IMMUNITY , sp - > id ) //100% sure spell immunity
| | ( ( * it ) - > count - 1 ) * ( * it ) - > MaxHealth ( ) + ( * it ) - > firstHPleft
>
2012-01-03 04:55:26 +03:00
usedSpellPower * 25 + sp - > powers [ spellLevel ]
2011-02-22 15:44:34 +02:00
)
{
ret . push_back ( ( * it ) - > ID ) ;
}
}
}
return ret ;
}
2011-03-05 18:38:22 +02:00
int BattleInfo : : theOtherPlayer ( int player ) const
{
return sides [ ! whatSide ( player ) ] ;
}
ui8 BattleInfo : : whatSide ( int player ) const
{
for ( int i = 0 ; i < ARRAY_COUNT ( sides ) ; i + + )
if ( sides [ i ] = = player )
return i ;
tlog1 < < " BattleInfo::whatSide: Player " < < player < < " is not in battle! \n " ;
return - 1 ;
}
2012-02-29 04:31:48 +03:00
int BattleInfo : : getIdForNewStack ( ) const
{
if ( stacks . size ( ) )
{
//stacks vector may be sorted not by ID and they may be not contiguous -> find stack with max ID
auto highestIDStack = * std : : max_element ( stacks . begin ( ) , stacks . end ( ) ,
[ ] ( const CStack * a , const CStack * b ) { return a - > ID < b - > ID ; } ) ;
return highestIDStack - > ID + 1 ;
}
return 0 ;
}
2012-05-18 23:50:16 +03:00
shared_ptr < CObstacleInstance > BattleInfo : : getObstacleOnTile ( BattleHex tile ) const
2012-03-31 00:36:07 +03:00
{
2012-05-18 23:50:16 +03:00
BOOST_FOREACH ( auto & obs , obstacles )
if ( vstd : : contains ( obs - > getAffectedTiles ( ) , tile ) )
return obs ;
2012-06-23 20:19:50 +03:00
return shared_ptr < CObstacleInstance > ( ) ;
2012-03-31 00:36:07 +03:00
}
2012-04-23 22:56:37 +03:00
int BattleInfo : : battlefieldTypeToBI ( int bfieldType )
{
static const std : : map < int , int > theMap = boost : : assign : : map_list_of ( 19 , BattlefieldBI : : CLOVER_FIELD )
( 22 , BattlefieldBI : : CURSED_GROUND ) ( 20 , BattlefieldBI : : EVIL_FOG ) ( 21 , BattlefieldBI : : NONE )
( 14 , BattlefieldBI : : FIERY_FIELDS ) ( 18 , BattlefieldBI : : HOLY_GROUND ) ( 17 , BattlefieldBI : : LUCID_POOLS )
( 16 , BattlefieldBI : : MAGIC_CLOUDS ) ( 9 , BattlefieldBI : : MAGIC_PLAINS ) ( 15 , BattlefieldBI : : ROCKLANDS )
( 1 , BattlefieldBI : : COASTAL ) ;
auto itr = theMap . find ( bfieldType ) ;
if ( itr ! = theMap . end ( ) )
return itr - > second ;
return BattlefieldBI : : NONE ;
}
2012-08-26 12:07:48 +03:00
CStack * BattleInfo : : getStack ( int stackID , bool onlyAlive /*= true*/ )
2012-05-05 00:16:39 +03:00
{
2012-08-26 12:07:48 +03:00
return const_cast < CStack * > ( battleGetStackByID ( stackID , onlyAlive ) ) ;
2012-05-05 00:16:39 +03:00
}
2012-08-26 12:07:48 +03:00
CStack * BattleInfo : : getStackT ( BattleHex tileID , bool onlyAlive /*= true*/ )
2012-05-18 23:50:16 +03:00
{
2012-08-26 12:07:48 +03:00
return const_cast < CStack * > ( battleGetStackByPos ( tileID , onlyAlive ) ) ;
2012-05-18 23:50:16 +03:00
}
2012-08-26 12:07:48 +03:00
BattleInfo : : BattleInfo ( )
2012-05-18 23:50:16 +03:00
{
2012-08-26 12:07:48 +03:00
setBattle ( this ) ;
setNodeType ( BATTLE ) ;
2012-05-18 23:50:16 +03:00
}
2010-12-25 21:23:30 +02:00
CStack : : CStack ( const CStackInstance * Base , int O , int I , bool AO , int S )
2011-01-07 12:48:31 +02:00
: base ( Base ) , ID ( I ) , owner ( O ) , slot ( S ) , attackerOwned ( AO ) ,
2010-12-25 21:23:30 +02:00
counterAttacks ( 1 )
{
assert ( base ) ;
type = base - > type ;
count = baseAmount = base - > count ;
2011-07-13 21:39:02 +03:00
setNodeType ( STACK_BATTLE ) ;
2010-12-25 21:23:30 +02:00
}
CStack : : CStack ( )
{
init ( ) ;
2011-07-13 21:39:02 +03:00
setNodeType ( STACK_BATTLE ) ;
2010-12-25 21:23:30 +02:00
}
CStack : : CStack ( const CStackBasicDescriptor * stack , int O , int I , bool AO , int S )
2011-01-07 12:48:31 +02:00
: base ( NULL ) , ID ( I ) , owner ( O ) , slot ( S ) , attackerOwned ( AO ) , counterAttacks ( 1 )
2010-12-25 21:23:30 +02:00
{
type = stack - > type ;
count = baseAmount = stack - > count ;
2011-07-13 21:39:02 +03:00
setNodeType ( STACK_BATTLE ) ;
2010-12-25 21:23:30 +02:00
}
void CStack : : init ( )
{
base = NULL ;
type = NULL ;
ID = - 1 ;
count = baseAmount = - 1 ;
firstHPleft = - 1 ;
owner = 255 ;
slot = 255 ;
attackerOwned = false ;
2011-12-22 16:05:19 +03:00
position = BattleHex ( ) ;
2010-12-25 21:23:30 +02:00
counterAttacks = - 1 ;
}
void CStack : : postInit ( )
{
assert ( type ) ;
2011-07-13 21:39:02 +03:00
assert ( getParentNodes ( ) . size ( ) ) ;
2010-12-25 21:23:30 +02:00
2011-10-02 10:16:34 +03:00
firstHPleft = MaxHealth ( ) ;
2011-04-12 11:59:18 +03:00
shots = getCreature ( ) - > valOfBonuses ( Bonus : : SHOTS ) ;
2010-12-25 21:23:30 +02:00
counterAttacks = 1 + valOfBonuses ( Bonus : : ADDITIONAL_RETALIATION ) ;
2012-05-07 15:54:22 +03:00
casts = valOfBonuses ( Bonus : : CASTS ) ;
2011-12-14 00:23:17 +03:00
state . insert ( EBattleStackState : : ALIVE ) ; //alive state indication
2010-12-25 21:23:30 +02:00
}
2011-10-17 11:24:51 +03:00
ui32 CStack : : Speed ( int turn /*= 0*/ , bool useBind /* = false*/ ) const
2010-12-25 21:23:30 +02:00
{
if ( hasBonus ( Selector : : type ( Bonus : : SIEGE_WEAPON ) & & Selector : : turns ( turn ) ) ) //war machines cannot move
return 0 ;
int speed = valOfBonuses ( Selector : : type ( Bonus : : STACKS_SPEED ) & & Selector : : turns ( turn ) ) ;
int percentBonus = 0 ;
2011-07-13 21:39:02 +03:00
BOOST_FOREACH ( const Bonus * b , getBonusList ( ) )
2010-12-25 21:23:30 +02:00
{
if ( b - > type = = Bonus : : STACKS_SPEED )
{
percentBonus + = b - > additionalInfo ;
}
}
speed = ( ( 100 + percentBonus ) * speed ) / 100 ;
2011-10-17 11:24:51 +03:00
//bind effect check - doesn't influence stack initiative
2012-08-20 19:10:50 +03:00
if ( useBind & & getEffect ( Spells : : BIND ) )
2010-12-25 21:23:30 +02:00
{
return 0 ;
}
return speed ;
}
2011-10-20 14:03:04 +03:00
si32 CStack : : magicResistance ( ) const
{
2011-10-22 10:05:57 +03:00
si32 magicResistance ;
if ( base ) //TODO: make war machines receive aura of magic resistance
{
magicResistance = base - > magicResistance ( ) ;
int auraBonus = 0 ;
2012-08-26 12:07:48 +03:00
BOOST_FOREACH ( const CStack * stack , base - > armyObj - > battle - > batteAdjacentCreatures ( this ) )
2011-10-20 14:03:04 +03:00
{
if ( stack - > owner = = owner )
{
2011-12-14 00:23:17 +03:00
vstd : : amax ( auraBonus , stack - > valOfBonuses ( Bonus : : SPELL_RESISTANCE_AURA ) ) ; //max value
2011-10-20 14:03:04 +03:00
}
}
2011-10-22 10:05:57 +03:00
magicResistance + = auraBonus ;
2011-12-14 00:23:17 +03:00
vstd : : amin ( magicResistance , 100 ) ;
2011-10-22 10:05:57 +03:00
}
else
magicResistance = type - > magicResistance ( ) ;
2011-10-20 14:03:04 +03:00
return magicResistance ;
}
2010-12-25 21:23:30 +02:00
const Bonus * CStack : : getEffect ( ui16 id , int turn /*= 0*/ ) const
{
2011-07-13 21:39:02 +03:00
BOOST_FOREACH ( Bonus * it , getBonusList ( ) )
2010-12-25 21:23:30 +02:00
{
2011-02-20 20:32:39 +02:00
if ( it - > source = = Bonus : : SPELL_EFFECT & & it - > sid = = id )
2010-12-25 21:23:30 +02:00
{
if ( ! turn | | it - > turnsRemain > turn )
return & ( * it ) ;
}
}
return NULL ;
}
2011-01-20 21:57:12 +02:00
void CStack : : stackEffectToFeature ( std : : vector < Bonus > & sf , const Bonus & sse )
2011-04-23 12:57:51 +03:00
{
2011-02-20 20:32:39 +02:00
si32 power = VLC - > spellh - > spells [ sse . sid ] - > powers [ sse . val ] ;
switch ( sse . sid )
2011-01-20 21:57:12 +02:00
{
case 27 : //shield
sf . push_back ( featureGenerator ( Bonus : : GENERAL_DAMAGE_REDUCTION , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 28 : //air shield
sf . push_back ( featureGenerator ( Bonus : : GENERAL_DAMAGE_REDUCTION , 1 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 29 : //fire shield
sf . push_back ( featureGenerator ( Bonus : : FIRE_SHIELD , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 30 : //protection from air
sf . push_back ( featureGenerator ( Bonus : : SPELL_DAMAGE_REDUCTION , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 31 : //protection from fire
sf . push_back ( featureGenerator ( Bonus : : SPELL_DAMAGE_REDUCTION , 1 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 32 : //protection from water
sf . push_back ( featureGenerator ( Bonus : : SPELL_DAMAGE_REDUCTION , 2 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 33 : //protection from earth
sf . push_back ( featureGenerator ( Bonus : : SPELL_DAMAGE_REDUCTION , 3 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 34 : //anti-magic
2011-12-14 00:23:17 +03:00
sf . push_back ( featureGenerator ( Bonus : : LEVEL_SPELL_IMMUNITY , GameConstants : : SPELL_LEVELS , power - 1 , sse . turnsRemain ) ) ;
2011-02-21 18:53:23 +02:00
sf . back ( ) . valType = Bonus : : INDEPENDENT_MAX ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
2011-07-16 11:28:01 +03:00
case 36 : //magic mirror
sf . push_back ( featureGenerator ( Bonus : : MAGIC_MIRROR , - 1 , power , sse . turnsRemain ) ) ;
sf . back ( ) . valType = Bonus : : INDEPENDENT_MAX ;
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
case 41 : //bless
2011-09-06 16:59:26 +03:00
sf . push_back ( featureGenerator ( Bonus : : ALWAYS_MAXIMUM_DAMAGE , - 1 , power , sse . turnsRemain ) ) ;
sf . back ( ) . valType = Bonus : : INDEPENDENT_MAX ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 42 : //curse
2011-09-06 16:59:26 +03:00
sf . push_back ( featureGenerator ( Bonus : : ALWAYS_MINIMUM_DAMAGE , - 1 , power , sse . turnsRemain , sse . val > = 2 ? 20 : 0 ) ) ;
sf . back ( ) . valType = Bonus : : INDEPENDENT_MAX ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 43 : //bloodlust
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK , power , sse . turnsRemain , 0 , Bonus : : ONLY_MELEE_FIGHT ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 44 : //precision
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK , power , sse . turnsRemain , 0 , Bonus : : ONLY_DISTANCE_FIGHT ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 45 : //weakness
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK , - 1 * power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 46 : //stone skin
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : DEFENSE , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 47 : //disrupting ray
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : DEFENSE , - 1 * power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-02-10 16:44:21 +02:00
sf . back ( ) . valType = Bonus : : ADDITIVE_VALUE ;
2011-01-20 21:57:12 +02:00
break ;
case 48 : //prayer
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : DEFENSE , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
sf . push_back ( featureGenerator ( Bonus : : STACKS_SPEED , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 49 : //mirth
sf . push_back ( featureGenerator ( Bonus : : MORALE , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 50 : //sorrow
sf . push_back ( featureGenerator ( Bonus : : MORALE , 0 , - 1 * power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 51 : //fortune
sf . push_back ( featureGenerator ( Bonus : : LUCK , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 52 : //misfortune
sf . push_back ( featureGenerator ( Bonus : : LUCK , 0 , - 1 * power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 53 : //haste
sf . push_back ( featureGenerator ( Bonus : : STACKS_SPEED , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 54 : //slow
sf . push_back ( featureGeneratorVT ( Bonus : : STACKS_SPEED , 0 , - 1 * ( 100 - power ) , sse . turnsRemain , Bonus : : PERCENT_TO_ALL ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 55 : //slayer
sf . push_back ( featureGenerator ( Bonus : : SLAYER , 0 , sse . val , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 56 : //frenzy
sf . push_back ( featureGenerator ( Bonus : : IN_FRENZY , 0 , VLC - > spellh - > spells [ 56 ] - > powers [ sse . val ] / 100.0 , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 58 : //counterstrike
sf . push_back ( featureGenerator ( Bonus : : ADDITIONAL_RETALIATION , 0 , power , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 59 : //bersek
sf . push_back ( featureGenerator ( Bonus : : ATTACKS_NEAREST_CREATURE , 0 , sse . val , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 60 : //hypnotize
sf . push_back ( featureGenerator ( Bonus : : HYPNOTIZED , 0 , sse . val , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
case 61 : //forgetfulness
sf . push_back ( featureGenerator ( Bonus : : FORGETFULL , 0 , sse . val , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
2012-02-20 14:02:19 +03:00
case Spells : : BLIND : //blind
sf . push_back ( makeFeatureVal ( Bonus : : NOT_ACTIVE , Bonus : : UNITL_BEING_ATTACKED | Bonus : : N_TURNS , sse . sid , 0 , Bonus : : SPELL_EFFECT , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
sf . push_back ( makeFeatureVal ( Bonus : : GENERAL_ATTACK_REDUCTION , Bonus : : UNTIL_ATTACK | Bonus : : N_TURNS , 0 , power , Bonus : : SPELL_EFFECT , sse . turnsRemain ) ) ;
2011-02-20 20:32:39 +02:00
sf . back ( ) . sid = sse . sid ;
2012-02-20 14:02:19 +03:00
sf . push_back ( makeFeatureVal ( Bonus : : NO_RETALIATION , Bonus : : UNITL_BEING_ATTACKED , 0 , 0 , Bonus : : SPELL_EFFECT , 0 ) ) ; // don't retaliate after basilisk / unicorn attack
sf . back ( ) . sid = sse . sid ;
2011-01-20 21:57:12 +02:00
break ;
2012-02-20 14:02:19 +03:00
case Spells : : STONE_GAZE : //Stone Gaze
case Spells : : PARALYZE : //Paralyze
sf . push_back ( makeFeatureVal ( Bonus : : NOT_ACTIVE , Bonus : : UNITL_BEING_ATTACKED | Bonus : : N_TURNS , sse . sid , 0 , Bonus : : SPELL_EFFECT , sse . turnsRemain ) ) ;
sf . back ( ) . sid = sse . sid ;
sf . push_back ( makeFeatureVal ( Bonus : : NO_RETALIATION , Bonus : : UNITL_BEING_ATTACKED , 0 , 0 , Bonus : : SPELL_EFFECT , 0 ) ) ; // don't retaliate after basilisk / unicorn attack
2011-04-25 12:03:13 +03:00
sf . back ( ) . sid = sse . sid ;
break ;
case 71 : //Poison
2011-07-08 17:54:20 +03:00
sf . push_back ( featureGeneratorVT ( Bonus : : POISON , 0 , 30 , sse . turnsRemain , Bonus : : INDEPENDENT_MAX ) ) ; //max hp penalty from this source
2011-04-25 12:03:13 +03:00
sf . back ( ) . sid = sse . sid ;
2011-07-08 17:54:20 +03:00
sf . push_back ( featureGeneratorVT ( Bonus : : STACK_HEALTH , 0 , - 10 , sse . turnsRemain , Bonus : : PERCENT_TO_ALL ) ) ;
2011-04-25 12:03:13 +03:00
sf . back ( ) . sid = sse . sid ;
break ;
case 72 : //Bind
2011-10-17 11:24:51 +03:00
sf . push_back ( featureGenerator ( Bonus : : BIND_EFFECT , 0 , 0 , 1 ) ) ; //marker
2011-04-25 12:03:13 +03:00
sf . back ( ) . duration = Bonus : : PERMANENT ;
sf . back ( ) . sid = sse . sid ;
break ;
case 73 : //Disease
2011-07-08 17:54:20 +03:00
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK , - 2 , sse . turnsRemain ) ) ;
2011-04-25 12:03:13 +03:00
sf . back ( ) . sid = sse . sid ;
2011-07-08 17:54:20 +03:00
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : DEFENSE , - 2 , sse . turnsRemain ) ) ;
2011-04-25 12:03:13 +03:00
sf . back ( ) . sid = sse . sid ;
break ;
case 75 : //Age
2011-07-08 17:54:20 +03:00
sf . push_back ( featureGeneratorVT ( Bonus : : STACK_HEALTH , 0 , - 50 , sse . turnsRemain , Bonus : : PERCENT_TO_ALL ) ) ;
2011-04-25 12:03:13 +03:00
sf . back ( ) . sid = sse . sid ;
break ;
2011-05-13 12:02:16 +03:00
case 80 : //Acid Breath
2011-07-08 17:54:20 +03:00
sf . push_back ( featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : DEFENSE , - sse . turnsRemain , 1 ) ) ;
2011-05-13 12:02:16 +03:00
sf . back ( ) . sid = sse . sid ;
sf . back ( ) . duration = Bonus : : PERMANENT ;
sf . back ( ) . valType = Bonus : : ADDITIVE_VALUE ;
break ;
2011-01-20 21:57:12 +02:00
}
2010-12-25 21:23:30 +02:00
}
ui8 CStack : : howManyEffectsSet ( ui16 id ) const
{
ui8 ret = 0 ;
2011-07-13 21:39:02 +03:00
BOOST_FOREACH ( const Bonus * it , getBonusList ( ) )
2011-02-20 20:32:39 +02:00
if ( it - > source = = Bonus : : SPELL_EFFECT & & it - > sid = = id ) //effect found
2010-12-25 21:23:30 +02:00
{
+ + ret ;
}
return ret ;
}
bool CStack : : willMove ( int turn /*= 0*/ ) const
{
2011-12-14 00:23:17 +03:00
return ( turn ? true : ! vstd : : contains ( state , EBattleStackState : : DEFENDING ) )
2010-12-25 21:23:30 +02:00
& & ! moved ( turn )
& & canMove ( turn ) ;
}
bool CStack : : canMove ( int turn /*= 0*/ ) const
{
return alive ( )
& & ! hasBonus ( Selector : : type ( Bonus : : NOT_ACTIVE ) & & Selector : : turns ( turn ) ) ; //eg. Ammo Cart or blinded creature
}
bool CStack : : moved ( int turn /*= 0*/ ) const
{
if ( ! turn )
2011-12-14 00:23:17 +03:00
return vstd : : contains ( state , EBattleStackState : : MOVED ) ;
2010-12-25 21:23:30 +02:00
else
return false ;
}
bool CStack : : doubleWide ( ) const
{
return getCreature ( ) - > doubleWide ;
}
2011-12-22 16:05:19 +03:00
BattleHex CStack : : occupiedHex ( ) const
2012-08-26 12:07:48 +03:00
{
return occupiedHex ( position ) ;
}
BattleHex CStack : : occupiedHex ( BattleHex assumedPos ) const
2010-12-25 21:23:30 +02:00
{
if ( doubleWide ( ) )
{
if ( attackerOwned )
2012-08-26 12:07:48 +03:00
return assumedPos - 1 ;
2010-12-25 21:23:30 +02:00
else
2012-08-26 12:07:48 +03:00
return assumedPos + 1 ;
2010-12-25 21:23:30 +02:00
}
else
{
2011-12-22 16:05:19 +03:00
return BattleHex : : INVALID ;
2010-12-25 21:23:30 +02:00
}
}
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > CStack : : getHexes ( ) const
2012-08-26 12:07:48 +03:00
{
return getHexes ( position ) ;
}
std : : vector < BattleHex > CStack : : getHexes ( BattleHex assumedPos ) const
{
return getHexes ( assumedPos , doubleWide ( ) , attackerOwned ) ;
}
std : : vector < BattleHex > CStack : : getHexes ( BattleHex assumedPos , bool twoHex , bool AttackerOwned )
2011-07-03 08:55:57 +03:00
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > hexes ;
2012-08-26 12:07:48 +03:00
hexes . push_back ( assumedPos ) ;
if ( twoHex )
{
if ( AttackerOwned )
hexes . push_back ( assumedPos - 1 ) ;
else
hexes . push_back ( assumedPos + 1 ) ;
}
2011-07-03 22:10:36 +03:00
2011-07-03 08:55:57 +03:00
return hexes ;
}
2011-12-22 16:05:19 +03:00
bool CStack : : coversPos ( BattleHex pos ) const
2011-07-03 08:55:57 +03:00
{
2011-07-03 22:10:36 +03:00
return vstd : : contains ( getHexes ( ) , pos ) ;
2011-07-03 08:55:57 +03:00
}
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > CStack : : getSurroundingHexes ( BattleHex attackerPos ) const
2011-07-04 22:34:49 +03:00
{
2011-12-22 16:05:19 +03:00
BattleHex hex = ( attackerPos ! = BattleHex : : INVALID ) ? attackerPos : position ; //use hypothetical position
std : : vector < BattleHex > hexes ;
2011-07-04 22:34:49 +03:00
if ( doubleWide ( ) )
{
2011-12-14 00:23:17 +03:00
const int WN = GameConstants : : BFIELD_WIDTH ;
2011-07-04 22:34:49 +03:00
if ( attackerOwned )
{ //position is equal to front hex
2011-12-22 16:05:19 +03:00
BattleHex : : checkAndPush ( hex - ( ( hex / WN ) % 2 ? WN + 2 : WN + 1 ) , hexes ) ;
BattleHex : : checkAndPush ( hex - ( ( hex / WN ) % 2 ? WN + 1 : WN ) , hexes ) ;
BattleHex : : checkAndPush ( hex - ( ( hex / WN ) % 2 ? WN : WN - 1 ) , hexes ) ;
BattleHex : : checkAndPush ( hex - 2 , hexes ) ;
BattleHex : : checkAndPush ( hex + 1 , hexes ) ;
BattleHex : : checkAndPush ( hex + ( ( hex / WN ) % 2 ? WN - 2 : WN - 1 ) , hexes ) ;
BattleHex : : checkAndPush ( hex + ( ( hex / WN ) % 2 ? WN - 1 : WN ) , hexes ) ;
BattleHex : : checkAndPush ( hex + ( ( hex / WN ) % 2 ? WN : WN + 1 ) , hexes ) ;
2011-07-04 22:34:49 +03:00
}
else
{
2011-12-22 16:05:19 +03:00
BattleHex : : checkAndPush ( hex - ( ( hex / WN ) % 2 ? WN + 1 : WN ) , hexes ) ;
BattleHex : : checkAndPush ( hex - ( ( hex / WN ) % 2 ? WN : WN - 1 ) , hexes ) ;
BattleHex : : checkAndPush ( hex - ( ( hex / WN ) % 2 ? WN - 1 : WN - 2 ) , hexes ) ;
BattleHex : : checkAndPush ( hex + 2 , hexes ) ;
BattleHex : : checkAndPush ( hex - 1 , hexes ) ;
BattleHex : : checkAndPush ( hex + ( ( hex / WN ) % 2 ? WN - 1 : WN ) , hexes ) ;
BattleHex : : checkAndPush ( hex + ( ( hex / WN ) % 2 ? WN : WN + 1 ) , hexes ) ;
BattleHex : : checkAndPush ( hex + ( ( hex / WN ) % 2 ? WN + 1 : WN + 2 ) , hexes ) ;
2011-07-04 22:34:49 +03:00
}
return hexes ;
}
else
{
2011-09-10 16:04:20 +03:00
return hex . neighbouringTiles ( ) ;
2011-07-04 22:34:49 +03:00
}
}
2010-12-25 21:23:30 +02:00
std : : vector < si32 > CStack : : activeSpells ( ) const
{
std : : vector < si32 > ret ;
2011-09-06 16:59:26 +03:00
TBonusListPtr spellEffects = getSpellBonuses ( ) ;
2011-06-25 16:53:15 +03:00
BOOST_FOREACH ( const Bonus * it , * spellEffects )
2010-12-25 21:23:30 +02:00
{
2011-02-20 20:32:39 +02:00
if ( ! vstd : : contains ( ret , it - > sid ) ) //do not duplicate spells with multiple effects
ret . push_back ( it - > sid ) ;
2010-12-25 21:23:30 +02:00
}
return ret ;
}
CStack : : ~ CStack ( )
{
detachFromAll ( ) ;
}
const CGHeroInstance * CStack : : getMyHero ( ) const
{
if ( base )
return dynamic_cast < const CGHeroInstance * > ( base - > armyObj ) ;
else //we are attached directly?
2011-07-13 21:39:02 +03:00
BOOST_FOREACH ( const CBonusSystemNode * n , getParentNodes ( ) )
2012-05-30 02:22:28 +03:00
if ( n - > getNodeType ( ) = = HERO )
return dynamic_cast < const CGHeroInstance * > ( n ) ;
2010-12-25 21:23:30 +02:00
return NULL ;
}
std : : string CStack : : nodeName ( ) const
{
std : : ostringstream oss ;
oss < < " Battle stack [ " < < ID < < " ]: " < < count < < " creatures of " ;
if ( type )
oss < < type - > namePl ;
else
oss < < " [UNDEFINED TYPE] " ;
oss < < " from slot " < < ( int ) slot ;
if ( base & & base - > armyObj )
oss < < " of armyobj= " < < base - > armyObj - > id ;
return oss . str ( ) ;
}
2011-01-08 20:33:40 +02:00
void CStack : : prepareAttacked ( BattleStackAttacked & bsa ) const
{
bsa . killedAmount = bsa . damageAmount / MaxHealth ( ) ;
unsigned damageFirst = bsa . damageAmount % MaxHealth ( ) ;
2012-02-10 16:13:24 +03:00
if ( bsa . damageAmount & & vstd : : contains ( state , EBattleStackState : : CLONED ) ) // block ability should not kill clone (0 damage)
{
bsa . killedAmount = count ;
2012-02-18 20:39:47 +03:00
bsa . flags | = BattleStackAttacked : : CLONE_KILLED ;
2012-02-10 16:13:24 +03:00
return ; // no rebirth I believe
}
2011-01-08 20:33:40 +02:00
if ( firstHPleft < = damageFirst )
{
bsa . killedAmount + + ;
bsa . newHP = firstHPleft + MaxHealth ( ) - damageFirst ;
}
else
{
bsa . newHP = firstHPleft - damageFirst ;
}
if ( count < = bsa . killedAmount ) //stack killed
{
bsa . newAmount = 0 ;
2011-02-24 17:33:03 +02:00
bsa . flags | = BattleStackAttacked : : KILLED ;
2011-01-08 20:33:40 +02:00
bsa . killedAmount = count ; //we cannot kill more creatures than we have
2011-07-08 17:54:20 +03:00
int resurrectFactor = valOfBonuses ( Bonus : : REBIRTH ) ;
if ( resurrectFactor > 0 & & casts ) //there must be casts left
{
int resurrectedCount = base - > count * resurrectFactor / 100 ;
if ( resurrectedCount )
2011-12-14 00:23:17 +03:00
resurrectedCount + = ( ( base - > count * resurrectFactor / 100.0 - resurrectedCount ) > ran ( ) % 100 / 100.0 ) ? 1 : 0 ; //last stack has proportional chance to rebirth
2011-07-08 17:54:20 +03:00
else //only one unit
2011-12-14 00:23:17 +03:00
resurrectedCount + = ( ( base - > count * resurrectFactor / 100.0 ) > ran ( ) % 100 / 100.0 ) ? 1 : 0 ;
2011-07-10 01:42:39 +03:00
if ( hasBonusOfType ( Bonus : : REBIRTH , 1 ) )
2011-12-14 00:23:17 +03:00
vstd : : amax ( resurrectedCount , 1 ) ; //resurrect at least one Sacred Phoenix
2011-07-08 17:54:20 +03:00
if ( resurrectedCount )
{
bsa . flags | = BattleStackAttacked : : REBIRTH ;
bsa . newAmount = resurrectedCount ; //risky?
2011-07-10 11:22:20 +03:00
bsa . newHP = MaxHealth ( ) ; //resore full health
2011-07-08 17:54:20 +03:00
}
}
2011-01-08 20:33:40 +02:00
}
else
{
bsa . newAmount = count - bsa . killedAmount ;
}
}
2011-12-22 16:05:19 +03:00
bool CStack : : isMeleeAttackPossible ( const CStack * attacker , const CStack * defender , BattleHex attackerPos /*= BattleHex::INVALID*/ , BattleHex defenderPos /*= BattleHex::INVALID*/ )
2011-01-08 20:33:40 +02:00
{
if ( ! attackerPos . isValid ( ) )
{
attackerPos = attacker - > position ;
}
if ( ! defenderPos . isValid ( ) )
{
defenderPos = defender - > position ;
}
return
2011-12-22 16:05:19 +03:00
( BattleHex : : mutualPosition ( attackerPos , defenderPos ) > = 0 ) //front <=> front
2011-01-08 20:33:40 +02:00
| | ( attacker - > doubleWide ( ) //back <=> front
2011-12-22 16:05:19 +03:00
& & BattleHex : : mutualPosition ( attackerPos + ( attacker - > attackerOwned ? - 1 : 1 ) , defenderPos ) > = 0 )
2011-01-08 20:33:40 +02:00
| | ( defender - > doubleWide ( ) //front <=> back
2011-12-22 16:05:19 +03:00
& & BattleHex : : mutualPosition ( attackerPos , defenderPos + ( defender - > attackerOwned ? - 1 : 1 ) ) > = 0 )
2011-01-08 20:33:40 +02:00
| | ( defender - > doubleWide ( ) & & attacker - > doubleWide ( ) //back <=> back
2011-12-22 16:05:19 +03:00
& & BattleHex : : mutualPosition ( attackerPos + ( attacker - > attackerOwned ? - 1 : 1 ) , defenderPos + ( defender - > attackerOwned ? - 1 : 1 ) ) > = 0 ) ;
2011-01-08 20:33:40 +02:00
}
2011-09-01 04:40:46 +03:00
bool CStack : : ableToRetaliate ( ) const
{
return alive ( )
& & ( counterAttacks > 0 | | hasBonusOfType ( Bonus : : UNLIMITED_RETALIATIONS ) )
& & ! hasBonusOfType ( Bonus : : SIEGE_WEAPON )
2012-02-20 14:02:19 +03:00
& & ! hasBonusOfType ( Bonus : : HYPNOTIZED )
& & ! hasBonusOfType ( Bonus : : NO_RETALIATION ) ;
2011-09-01 04:40:46 +03:00
}
2011-10-22 10:05:57 +03:00
std : : string CStack : : getName ( ) const
{
return ( count > 1 ) ? type - > namePl : type - > nameSing ; //War machines can't use base
}
2012-02-17 00:19:07 +03:00
bool CStack : : isValidTarget ( bool allowDead /* = false*/ ) const /*alive non-turret stacks (can be attacked or be object of magic effect) */
{
return ( alive ( ) | | allowDead ) & & position . isValid ( ) ;
}
2012-03-31 00:36:07 +03:00
bool CStack : : canBeHealed ( ) const
{
2012-06-10 16:07:08 +03:00
return firstHPleft < MaxHealth ( )
& & isValidTarget ( )
2012-03-31 00:36:07 +03:00
& & ! hasBonusOfType ( Bonus : : SIEGE_WEAPON ) ;
}
2010-12-25 21:23:30 +02:00
bool CMP_stack : : operator ( ) ( const CStack * a , const CStack * b )
{
switch ( phase )
{
case 0 : //catapult moves after turrets
2011-05-18 20:51:10 +03:00
return a - > getCreature ( ) - > idNumber > b - > getCreature ( ) - > idNumber ; //catapult is 145 and turrets are 149
2010-12-25 21:23:30 +02:00
case 1 : //fastest first, upper slot first
{
int as = a - > Speed ( turn ) , bs = b - > Speed ( turn ) ;
if ( as ! = bs )
return as > bs ;
else
return a - > slot < b - > slot ;
}
case 2 : //fastest last, upper slot first
//TODO: should be replaced with order of receiving morale!
case 3 : //fastest last, upper slot first
{
int as = a - > Speed ( turn ) , bs = b - > Speed ( turn ) ;
if ( as ! = bs )
return as < bs ;
else
return a - > slot < b - > slot ;
}
default :
assert ( 0 ) ;
return false ;
}
}
CMP_stack : : CMP_stack ( int Phase /*= 1*/ , int Turn )
{
phase = Phase ;
turn = Turn ;
}