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"
2011-09-03 22:25:37 +03:00
# include "../lib/JsonNode.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 ;
getStackQueue ( hlp , 1 , - 1 ) ;
if ( hlp . size ( ) )
return hlp [ 0 ] ;
else
return NULL ;
}
static const CStack * takeStack ( std : : vector < const CStack * > & st , int & curside , int turn )
{
const CStack * ret = NULL ;
unsigned i , //fastest stack
j ; //fastest stack of the other side
for ( i = 0 ; i < st . size ( ) ; i + + )
if ( st [ i ] )
break ;
//no stacks left
if ( i = = st . size ( ) )
return NULL ;
const CStack * fastest = st [ i ] , * other = NULL ;
int bestSpeed = fastest - > Speed ( turn ) ;
if ( fastest - > attackerOwned ! = curside )
{
ret = fastest ;
}
else
{
for ( j = i + 1 ; j < st . size ( ) ; j + + )
{
if ( ! st [ j ] ) continue ;
if ( st [ j ] - > attackerOwned ! = curside | | st [ j ] - > Speed ( turn ) ! = bestSpeed )
break ;
}
if ( j > = st . size ( ) )
{
ret = fastest ;
}
else
{
other = st [ j ] ;
if ( other - > Speed ( turn ) ! = bestSpeed )
ret = fastest ;
else
ret = other ;
}
}
assert ( ret ) ;
if ( ret = = fastest )
st [ i ] = NULL ;
else
st [ j ] = NULL ;
curside = ret - > attackerOwned ;
return ret ;
}
CStack * BattleInfo : : getStack ( int stackID , bool onlyAlive )
{
2011-12-14 00:23:17 +03:00
for ( ui32 g = 0 ; g < stacks . size ( ) ; + + g )
2010-12-25 21:23:30 +02:00
{
if ( stacks [ g ] - > ID = = stackID & & ( ! onlyAlive | | stacks [ g ] - > alive ( ) ) )
return stacks [ g ] ;
}
return NULL ;
}
const CStack * BattleInfo : : getStack ( int stackID , bool onlyAlive ) const
{
return const_cast < BattleInfo * const > ( this ) - > getStack ( stackID , onlyAlive ) ;
}
2011-12-22 16:05:19 +03:00
CStack * BattleInfo : : getStackT ( BattleHex tileID , bool onlyAlive )
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
for ( ui32 g = 0 ; g < stacks . size ( ) ; + + g )
2010-12-25 21:23:30 +02:00
{
if ( stacks [ g ] - > position = = tileID
| | ( stacks [ g ] - > doubleWide ( ) & & stacks [ g ] - > attackerOwned & & stacks [ g ] - > position - 1 = = tileID )
| | ( stacks [ g ] - > doubleWide ( ) & & ! stacks [ g ] - > attackerOwned & & stacks [ g ] - > position + 1 = = tileID ) )
{
if ( ! onlyAlive | | stacks [ g ] - > alive ( ) )
{
return stacks [ g ] ;
}
}
}
return NULL ;
}
2011-12-22 16:05:19 +03:00
const CStack * BattleInfo : : getStackT ( BattleHex tileID , bool onlyAlive ) const
2010-12-25 21:23:30 +02:00
{
return const_cast < BattleInfo * const > ( this ) - > getStackT ( tileID , onlyAlive ) ;
}
2011-12-22 16:05:19 +03:00
void BattleInfo : : getAccessibilityMap ( bool * accessibility , bool twoHex , bool attackerOwned , bool addOccupiable , std : : set < BattleHex > & occupyable , bool flying , const CStack * stackToOmmit ) const
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
memset ( accessibility , 1 , GameConstants : : BFIELD_SIZE ) ; //initialize array with trues
2010-12-25 21:23:30 +02:00
//removing accessibility for side columns of hexes
2011-12-14 00:23:17 +03:00
for ( int v = 0 ; v < GameConstants : : BFIELD_SIZE ; + + v )
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
if ( v % GameConstants : : BFIELD_WIDTH = = 0 | | v % GameConstants : : BFIELD_WIDTH = = ( GameConstants : : BFIELD_WIDTH - 1 ) )
2010-12-25 21:23:30 +02:00
accessibility [ v ] = false ;
}
2011-12-14 00:23:17 +03:00
for ( ui32 g = 0 ; g < stacks . size ( ) ; + + g )
2010-12-25 21:23:30 +02:00
{
2011-02-10 16:44:21 +02:00
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)
2010-12-25 21:23:30 +02:00
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
2012-05-18 23:50:16 +03:00
BOOST_FOREACH ( const auto & obstacle , obstacles )
2010-12-25 21:23:30 +02:00
{
2012-05-18 23:50:16 +03:00
BOOST_FOREACH ( BattleHex hex , obstacle - > getBlockedTiles ( ) )
2010-12-25 21:23:30 +02:00
{
2012-05-18 23:50:16 +03:00
assert ( hex . isValid ( ) ) ;
accessibility [ hex ] = false ;
2010-12-25 21:23:30 +02:00
}
}
//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 ;
}
2011-12-22 16:05:19 +03:00
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 ) ) } ;
2010-12-25 21:23:30 +02:00
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 )
{
2011-12-22 16:05:19 +03:00
std : : set < BattleHex > rem ; //tiles to unlock
2011-12-14 00:23:17 +03:00
for ( int h = 0 ; h < GameConstants : : BFIELD_HEIGHT ; + + h )
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
for ( int w = 1 ; w < GameConstants : : BFIELD_WIDTH - 1 ; + + w )
2010-12-25 21:23:30 +02:00
{
2011-12-22 16:05:19 +03:00
BattleHex hex ( w , h ) ;
2010-12-25 21:23:30 +02:00
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 ;
} */
}
}
2011-12-22 16:05:19 +03:00
bool BattleInfo : : isAccessible ( BattleHex hex , bool * accessibility , bool twoHex , bool attackerOwned , bool flying , bool lastPos )
2010-12-25 21:23:30 +02:00
{
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 ] ;
}
}
2011-12-22 16:05:19 +03:00
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
2010-12-25 21:23:30 +02:00
{
2012-05-18 23:50:16 +03:00
std : : set < BattleHex > quicksands = getStoppers ( ! attackerOwned ) ;
2012-05-05 00:16:39 +03:00
2010-12-25 21:23:30 +02:00
//inits
2011-12-14 00:23:17 +03:00
for ( int b = 0 ; b < GameConstants : : BFIELD_SIZE ; + + b )
2010-12-25 21:23:30 +02:00
predecessor [ b ] = - 1 ;
2011-12-14 00:23:17 +03:00
for ( int g = 0 ; g < GameConstants : : BFIELD_SIZE ; + + g )
2010-12-25 21:23:30 +02:00
dists [ g ] = 100000000 ;
2011-12-22 16:05:19 +03:00
std : : queue < std : : pair < BattleHex , bool > > hexq ; //bfs queue <hex, accessible> (second filed used only if fillPredecessors is true)
2010-12-25 21:23:30 +02:00
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
{
2011-12-22 16:05:19 +03:00
std : : pair < BattleHex , bool > curHex = hexq . front ( ) ;
std : : vector < BattleHex > neighbours = curHex . first . neighbouringTiles ( ) ;
2010-12-25 21:23:30 +02:00
hexq . pop ( ) ;
2012-05-05 00:16:39 +03:00
if ( curHex . first ! = start & & ! flying & & vstd : : contains ( quicksands , curHex . first ) ) //walking stack can't step past the quicksands
continue ;
2011-12-14 00:23:17 +03:00
for ( ui32 nr = 0 ; nr < neighbours . size ( ) ; nr + + )
2010-12-25 21:23:30 +02:00
{
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 ;
}
}
} ;
2012-04-03 02:23:14 +03:00
std : : vector < BattleHex > BattleInfo : : getAccessibility ( const CStack * stack , bool addOccupiable , std : : vector < BattleHex > * attackable /*= NULL*/ , bool forPassingBy /*= false*/ ) const
2010-12-25 21:23:30 +02:00
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > ret ;
2011-12-14 00:23:17 +03:00
bool ac [ GameConstants : : BFIELD_SIZE ] ;
2010-12-25 21:23:30 +02:00
2011-01-07 12:48:31 +02:00
if ( stack - > position < 0 ) //turrets
2011-12-22 16:05:19 +03:00
return std : : vector < BattleHex > ( ) ;
2010-12-25 21:23:30 +02:00
2011-12-22 16:05:19 +03:00
std : : set < BattleHex > occupyable ;
2010-12-25 21:23:30 +02:00
2011-01-07 12:48:31 +02:00
getAccessibilityMap ( ac , stack - > doubleWide ( ) , stack - > attackerOwned , addOccupiable , occupyable , stack - > hasBonusOfType ( Bonus : : FLYING ) , stack ) ;
2010-12-25 21:23:30 +02:00
2011-12-22 16:05:19 +03:00
BattleHex pr [ GameConstants : : BFIELD_SIZE ] ;
2011-12-14 00:23:17 +03:00
int dist [ GameConstants : : BFIELD_SIZE ] ;
2011-01-07 12:48:31 +02:00
makeBFS ( stack - > position , ac , pr , dist , stack - > doubleWide ( ) , stack - > attackerOwned , stack - > hasBonusOfType ( Bonus : : FLYING ) , false ) ;
2010-12-25 21:23:30 +02:00
2011-01-07 12:48:31 +02:00
if ( stack - > doubleWide ( ) )
2010-12-25 21:23:30 +02:00
{
if ( ! addOccupiable )
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > rem ;
2011-12-14 00:23:17 +03:00
for ( int b = 0 ; b < GameConstants : : BFIELD_SIZE ; + + b )
2010-12-25 21:23:30 +02:00
{
//don't take into account most left and most right columns of hexes
2011-12-14 00:23:17 +03:00
if ( b % GameConstants : : BFIELD_WIDTH = = 0 | | b % GameConstants : : BFIELD_WIDTH = = GameConstants : : BFIELD_WIDTH - 1 )
2010-12-25 21:23:30 +02:00
continue ;
2011-01-07 12:48:31 +02:00
if ( ac [ b ] & & ! ( stack - > attackerOwned ? ac [ b - 1 ] : ac [ b + 1 ] ) )
2010-12-25 21:23:30 +02:00
{
rem . push_back ( b ) ;
}
}
2011-12-14 00:23:17 +03:00
for ( ui32 g = 0 ; g < rem . size ( ) ; + + g )
2010-12-25 21:23:30 +02:00
{
ac [ rem [ g ] ] = false ;
}
//removing accessibility for side hexes
2011-12-14 00:23:17 +03:00
for ( int v = 0 ; v < GameConstants : : BFIELD_SIZE ; + + v )
if ( stack - > attackerOwned ? ( v % GameConstants : : BFIELD_WIDTH ) = = 1 : ( v % GameConstants : : BFIELD_WIDTH ) = = ( GameConstants : : BFIELD_WIDTH - 2 ) )
2010-12-25 21:23:30 +02:00
ac [ v ] = false ;
}
}
2011-12-14 00:23:17 +03:00
for ( int i = 0 ; i < GameConstants : : BFIELD_SIZE ; + + i )
2011-02-12 18:12:48 +02:00
{
2011-10-17 11:24:51 +03:00
bool rangeFits ;
if ( tacticDistance )
2012-04-03 02:23:14 +03:00
{
rangeFits = pr [ i ] > = 0 ; //reachable in terms of obstacles
if ( ! forPassingBy ) //only if we're passing through, we may step out of the tactic range -> otherwise check range
rangeFits = rangeFits & & isInTacticRange ( i ) ;
}
2011-10-17 11:24:51 +03:00
else
rangeFits = dist [ i ] < = stack - > Speed ( 0 , true ) ; //we can reach the stack
2011-02-12 18:12:48 +02:00
if ( ( ! addOccupiable & & rangeFits & & ac [ i ] )
| | ( addOccupiable & & rangeFits & & isAccessible ( i , ac , stack - > doubleWide ( ) , stack - > attackerOwned , stack - > hasBonusOfType ( Bonus : : FLYING ) , true ) ) //we can reach it
2011-10-17 11:24:51 +03:00
| | ( vstd : : contains ( occupyable , i ) & & ( ! tacticDistance & & dist [ i + ( stack - > attackerOwned ? 1 : - 1 ) ] < = stack - > Speed ( 0 , true ) ) & & ac [ i + ( stack - > attackerOwned ? 1 : - 1 ) ] ) //it's occupyable and we can reach adjacent hex
2010-12-25 21:23:30 +02:00
)
{
ret . push_back ( i ) ;
}
}
2011-02-26 19:32:56 +02:00
if ( attackable )
{
struct HLP
{
2011-12-22 16:05:19 +03:00
static bool meleeAttackable ( BattleHex hex , const std : : vector < BattleHex > & baseRng )
2011-02-26 19:32:56 +02:00
{
2011-12-22 16:05:19 +03:00
BOOST_FOREACH ( BattleHex h , baseRng )
2011-02-26 19:32:56 +02:00
{
2011-12-22 16:05:19 +03:00
if ( BattleHex : : mutualPosition ( h , hex ) > 0 )
2011-02-26 19:32:56 +02:00
return true ;
}
return false ;
}
} ;
BOOST_FOREACH ( const CStack * otherSt , stacks )
{
2012-02-22 17:31:53 +03:00
if ( otherSt - > owner = = stack - > owner | | ! otherSt - > isValidTarget ( false ) )
2011-02-26 19:32:56 +02:00
continue ;
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > occupiedBySecond ;
2011-02-26 19:32:56 +02:00
occupiedBySecond . push_back ( otherSt - > position ) ;
if ( otherSt - > doubleWide ( ) )
occupiedBySecond . push_back ( otherSt - > occupiedHex ( ) ) ;
if ( battleCanShoot ( stack , otherSt - > position ) )
{
attackable - > insert ( attackable - > end ( ) , occupiedBySecond . begin ( ) , occupiedBySecond . end ( ) ) ;
continue ;
}
2011-12-22 16:05:19 +03:00
BOOST_FOREACH ( BattleHex he , occupiedBySecond )
2011-02-26 19:32:56 +02:00
{
if ( HLP : : meleeAttackable ( he , ret ) )
attackable - > push_back ( he ) ;
}
}
}
2010-12-25 21:23:30 +02:00
return ret ;
}
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
{
2011-12-14 00:23:17 +03:00
bool ac [ GameConstants : : BFIELD_SIZE ] ;
2012-04-22 20:38:36 +03:00
2011-10-01 22:56:54 +03:00
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-06-09 22:24:04 +03:00
std : : set < BattleHex > occupyable ;
2012-06-16 17:25:39 +03:00
getAccessibilityMap ( ac , twoHex , attackerOwned , true , occupyable , flying ) ;
2012-04-22 20:38:36 +03:00
2012-06-09 22:24:04 +03:00
for ( int i = 0 ; i < GameConstants : : BFIELD_SIZE ; + + i )
{
if ( ac [ i ] )
occupyable . insert ( i ) ;
}
2012-04-22 20:38:36 +03:00
if ( ! occupyable . size ( ) )
return - 1 ; //all tiles are covered
2011-10-01 22:56:54 +03:00
2012-06-16 17:25:39 +03:00
return getClosestTile ( attackerOwned , pos , occupyable ) ;
2011-10-01 22:56:54 +03:00
}
2011-01-08 20:33:40 +02:00
bool BattleInfo : : isStackBlocked ( const CStack * stack ) const
2010-12-25 21:23:30 +02:00
{
2011-01-07 12:48:31 +02:00
if ( stack - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) ) //siege weapons cannot be blocked
2010-12-25 21:23:30 +02:00
return false ;
2011-11-08 11:54:32 +03:00
BOOST_FOREACH ( CStack * s , getAdjacentCreatures ( stack ) )
2010-12-25 21:23:30 +02:00
{
2011-11-08 11:54:32 +03:00
if ( s - > owner ! = stack - > owner ) //blocked by enemy stack
return true ;
2010-12-25 21:23:30 +02:00
}
return false ;
}
2011-12-22 16:05:19 +03:00
std : : pair < std : : vector < BattleHex > , int > BattleInfo : : getPath ( BattleHex start , BattleHex dest , bool * accessibility , bool flyingCreature , bool twoHex , bool attackerOwned )
2011-01-07 12:48:31 +02:00
{
2011-12-22 16:05:19 +03:00
BattleHex predecessor [ GameConstants : : BFIELD_SIZE ] ; //for getting the Path
2011-12-14 00:23:17 +03:00
int dist [ GameConstants : : BFIELD_SIZE ] ; //calculated distances
2010-12-25 21:23:30 +02:00
makeBFS ( start , accessibility , predecessor , dist , twoHex , attackerOwned , flyingCreature , false ) ;
if ( predecessor [ dest ] = = - 1 ) //cannot reach destination
{
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 ) ;
curElem = predecessor [ curElem ] ;
}
return std : : make_pair ( path , dist [ dest ] ) ;
}
2011-07-06 20:00:45 +03:00
TDmgRange BattleInfo : : calculateDmgRange ( const CStack * attacker , const CStack * defender , TQuantity attackerCount , TQuantity defenderCount , const CGHeroInstance * attackerHero , const CGHeroInstance * defendingHero ,
bool shooting , ui8 charge , bool lucky , bool deathBlow , bool ballistaDoubleDmg ) const
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
double additiveBonus = 1.0 , multBonus = 1.0 ,
2011-01-08 20:33:40 +02:00
minDmg = attacker - > getMinDamage ( ) * attackerCount ,
maxDmg = attacker - > getMaxDamage ( ) * attackerCount ;
2010-12-25 21:23:30 +02:00
if ( attacker - > getCreature ( ) - > idNumber = = 149 ) //arrow turret
{
switch ( attacker - > position )
{
case - 2 : //keep
minDmg = 15 ;
maxDmg = 15 ;
break ;
case - 3 : case - 4 : //turrets
2011-12-14 00:23:17 +03:00
minDmg = 7.5 ;
maxDmg = 7.5 ;
2010-12-25 21:23:30 +02:00
break ;
}
}
if ( attacker - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) & & attacker - > getCreature ( ) - > idNumber ! = 149 ) //any siege weapon, but only ballista can attack (second condition - not arrow turret)
{ //minDmg and maxDmg are multiplied by hero attack + 1
minDmg * = attackerHero - > getPrimSkillLevel ( 0 ) + 1 ;
maxDmg * = attackerHero - > getPrimSkillLevel ( 0 ) + 1 ;
}
int attackDefenceDifference = 0 ;
if ( attacker - > hasBonusOfType ( Bonus : : GENERAL_ATTACK_REDUCTION ) )
{
2011-12-14 00:23:17 +03:00
double multAttackReduction = attacker - > valOfBonuses ( Bonus : : GENERAL_ATTACK_REDUCTION , - 1024 ) / 100.0 ;
2010-12-25 21:23:30 +02:00
attackDefenceDifference = attacker - > Attack ( ) * multAttackReduction ;
}
else
{
attackDefenceDifference = attacker - > Attack ( ) ;
}
if ( attacker - > hasBonusOfType ( Bonus : : ENEMY_DEFENCE_REDUCTION ) )
{
2011-12-14 00:23:17 +03:00
double multDefenceReduction = ( 100 - attacker - > valOfBonuses ( Bonus : : ENEMY_DEFENCE_REDUCTION , - 1024 ) ) / 100.0 ;
2010-12-25 21:23:30 +02:00
attackDefenceDifference - = defender - > Defense ( ) * multDefenceReduction ;
}
else
{
attackDefenceDifference - = defender - > Defense ( ) ;
}
//calculating total attack/defense skills modifier
if ( shooting ) //precision handling (etc.)
2011-06-25 16:53:15 +03:00
attackDefenceDifference + = attacker - > getBonuses ( Selector : : typeSubtype ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK ) , Selector : : effectRange ( Bonus : : ONLY_DISTANCE_FIGHT ) ) - > totalValue ( ) ;
2010-12-25 21:23:30 +02:00
else //bloodlust handling (etc.)
2011-06-25 16:53:15 +03:00
attackDefenceDifference + = attacker - > getBonuses ( Selector : : typeSubtype ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK ) , Selector : : effectRange ( Bonus : : ONLY_MELEE_FIGHT ) ) - > totalValue ( ) ;
2010-12-25 21:23:30 +02:00
if ( attacker - > getEffect ( 55 ) ) //slayer handling
{
std : : vector < int > affectedIds ;
int spLevel = attacker - > getEffect ( 55 ) - > val ;
for ( int g = 0 ; g < VLC - > creh - > creatures . size ( ) ; + + g )
{
2011-07-13 21:39:02 +03:00
BOOST_FOREACH ( const Bonus * b , VLC - > creh - > creatures [ g ] - > getBonusList ( ) )
2010-12-25 21:23:30 +02:00
{
if ( ( b - > type = = Bonus : : KING3 & & spLevel > = 3 ) | | //expert
( b - > type = = Bonus : : KING2 & & spLevel > = 2 ) | | //adv +
( b - > type = = Bonus : : KING1 & & spLevel > = 0 ) ) //none or basic +
{
affectedIds . push_back ( g ) ;
break ;
}
}
}
2011-12-14 00:23:17 +03:00
for ( ui32 g = 0 ; g < affectedIds . size ( ) ; + + g )
2010-12-25 21:23:30 +02:00
{
if ( defender - > getCreature ( ) - > idNumber = = affectedIds [ g ] )
{
attackDefenceDifference + = VLC - > spellh - > spells [ 55 ] - > powers [ attacker - > getEffect ( 55 ) - > val ] ;
break ;
}
}
}
//bonus from attack/defense skills
if ( attackDefenceDifference < 0 ) //decreasing dmg
{
2011-12-14 00:23:17 +03:00
double dec = 0.025 * ( - attackDefenceDifference ) ;
if ( dec > 0.7 )
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
multBonus * = 0.3 ; //1.0 - 0.7
2010-12-25 21:23:30 +02:00
}
else
{
2011-12-14 00:23:17 +03:00
multBonus * = 1.0 - dec ;
2010-12-25 21:23:30 +02:00
}
}
else //increasing dmg
{
2011-12-14 00:23:17 +03:00
double inc = 0.05 * attackDefenceDifference ;
if ( inc > 4.0 )
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
additiveBonus + = 4.0 ;
2010-12-25 21:23:30 +02:00
}
else
{
additiveBonus + = inc ;
}
}
//applying jousting bonus
if ( attacker - > hasBonusOfType ( Bonus : : JOUSTING ) & & ! defender - > hasBonusOfType ( Bonus : : CHARGE_IMMUNITY ) )
2011-12-14 00:23:17 +03:00
additiveBonus + = charge * 0.05 ;
2010-12-25 21:23:30 +02:00
//handling secondary abilities and artifacts giving premies to them
if ( attackerHero )
{
if ( shooting )
{
2011-12-14 00:23:17 +03:00
additiveBonus + = attackerHero - > valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , CGHeroInstance : : ARCHERY ) / 100.0 ;
2010-12-25 21:23:30 +02:00
}
else
{
2011-12-14 00:23:17 +03:00
additiveBonus + = attackerHero - > valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , CGHeroInstance : : OFFENCE ) / 100.0 ;
2010-12-25 21:23:30 +02:00
}
}
if ( defendingHero )
{
2011-12-14 00:23:17 +03:00
multBonus * = ( std : : max ( 0 , 100 - defendingHero - > valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , CGHeroInstance : : ARMORER ) ) ) / 100.0 ;
2010-12-25 21:23:30 +02:00
}
//handling hate effect
2011-12-14 00:23:17 +03:00
additiveBonus + = attacker - > valOfBonuses ( Bonus : : HATE , defender - > getCreature ( ) - > idNumber ) / 100. ;
2010-12-25 21:23:30 +02:00
//luck bonus
if ( lucky )
{
2011-12-14 00:23:17 +03:00
additiveBonus + = 1.0 ;
2010-12-25 21:23:30 +02:00
}
2011-02-24 17:33:03 +02:00
//ballista double dmg
if ( ballistaDoubleDmg )
{
2011-12-14 00:23:17 +03:00
additiveBonus + = 1.0 ;
2011-02-24 17:33:03 +02:00
}
2011-07-06 20:00:45 +03:00
if ( deathBlow ) //Dread Knight and many WoGified creatures
{
2011-12-14 00:23:17 +03:00
additiveBonus + = 1.0 ;
2011-07-06 20:00:45 +03:00
}
2010-12-25 21:23:30 +02:00
//handling spell effects
if ( ! shooting & & defender - > hasBonusOfType ( Bonus : : GENERAL_DAMAGE_REDUCTION , 0 ) ) //eg. shield
{
2011-12-14 00:23:17 +03:00
multBonus * = defender - > valOfBonuses ( Bonus : : GENERAL_DAMAGE_REDUCTION , 0 ) / 100.0 ;
2010-12-25 21:23:30 +02:00
}
else if ( shooting & & defender - > hasBonusOfType ( Bonus : : GENERAL_DAMAGE_REDUCTION , 1 ) ) //eg. air shield
{
2011-12-14 00:23:17 +03:00
multBonus * = defender - > valOfBonuses ( Bonus : : GENERAL_DAMAGE_REDUCTION , 1 ) / 100.0 ;
2010-12-25 21:23:30 +02:00
}
2011-09-06 16:59:26 +03:00
TBonusListPtr curseEffects = attacker - > getBonuses ( Selector : : type ( Bonus : : ALWAYS_MINIMUM_DAMAGE ) ) ; //attacker->getEffect(42);
TBonusListPtr blessEffects = attacker - > getBonuses ( Selector : : type ( Bonus : : ALWAYS_MAXIMUM_DAMAGE ) ) ; //attacker->getEffect(43);
int curseBlessAdditiveModifier = blessEffects - > totalValue ( ) - curseEffects - > totalValue ( ) ;
double curseMultiplicativePenalty = curseEffects - > size ( ) ? ( * std : : max_element ( curseEffects - > begin ( ) , curseEffects - > end ( ) , & Bonus : : compareByAdditionalInfo ) ) - > additionalInfo : 0 ;
if ( curseMultiplicativePenalty ) //curse handling (partial, the rest is below)
2010-12-25 21:23:30 +02:00
{
2011-09-06 16:59:26 +03:00
multBonus * = 1.0 - curseMultiplicativePenalty / 100 ;
2010-12-25 21:23:30 +02:00
}
class HLP
{
public :
static bool hasAdvancedAirShield ( const CStack * stack )
{
2011-07-13 21:39:02 +03:00
BOOST_FOREACH ( const Bonus * it , stack - > 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 = = 28 & & it - > val > = 2 )
2010-12-25 21:23:30 +02:00
{
return true ;
}
}
return false ;
}
} ;
//wall / distance penalty + advanced air shield
2011-06-22 15:44:28 +03:00
bool distPenalty = ! NBonus : : hasOfType ( attackerHero , Bonus : : NO_DISTANCE_PENALTY ) & &
hasDistancePenalty ( attacker , defender - > position ) ;
2011-10-20 20:41:40 +03:00
bool obstaclePenalty = hasWallPenalty ( attacker , defender - > position ) ;
if ( shooting )
2010-12-25 21:23:30 +02:00
{
2011-10-20 20:41:40 +03:00
if ( distPenalty | | HLP : : hasAdvancedAirShield ( defender ) )
{
multBonus * = 0.5 ;
}
if ( obstaclePenalty )
{
multBonus * = 0.5 ; //cumulative
}
2010-12-25 21:23:30 +02:00
}
if ( ! shooting & & attacker - > hasBonusOfType ( Bonus : : SHOOTER ) & & ! attacker - > hasBonusOfType ( Bonus : : NO_MELEE_PENALTY ) )
{
multBonus * = 0.5 ;
}
minDmg * = additiveBonus * multBonus ;
maxDmg * = additiveBonus * multBonus ;
2011-01-08 20:33:40 +02:00
TDmgRange returnedVal ;
2010-12-25 21:23:30 +02:00
2011-09-06 16:59:26 +03:00
if ( curseEffects - > size ( ) ) //curse handling (rest)
2010-12-25 21:23:30 +02:00
{
2011-09-06 16:59:26 +03:00
minDmg + = curseBlessAdditiveModifier ;
2010-12-25 21:23:30 +02:00
returnedVal = std : : make_pair ( int ( minDmg ) , int ( minDmg ) ) ;
}
2011-09-06 16:59:26 +03:00
else if ( blessEffects - > size ( ) ) //bless handling
2010-12-25 21:23:30 +02:00
{
2011-09-06 16:59:26 +03:00
maxDmg + = curseBlessAdditiveModifier ;
2010-12-25 21:23:30 +02:00
returnedVal = std : : make_pair ( int ( maxDmg ) , int ( maxDmg ) ) ;
}
else
{
returnedVal = std : : make_pair ( int ( minDmg ) , int ( maxDmg ) ) ;
}
//damage cannot be less than 1
2011-12-14 00:23:17 +03:00
vstd : : amax ( returnedVal . first , 1 ) ;
vstd : : amax ( returnedVal . second , 1 ) ;
2010-12-25 21:23:30 +02:00
return returnedVal ;
}
2011-07-06 20:00:45 +03:00
TDmgRange BattleInfo : : calculateDmgRange ( const CStack * attacker , const CStack * defender , const CGHeroInstance * attackerHero , const CGHeroInstance * defendingHero ,
bool shooting , ui8 charge , bool lucky , bool deathBlow , bool ballistaDoubleDmg ) const
2011-01-08 20:33:40 +02:00
{
2011-07-06 20:00:45 +03:00
return calculateDmgRange ( attacker , defender , attacker - > count , defender - > count , attackerHero , defendingHero , shooting , charge , lucky , deathBlow , ballistaDoubleDmg ) ;
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
{
2011-07-06 20:00:45 +03:00
TDmgRange range = calculateDmgRange ( attacker , defender , attackerHero , defendingHero , 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 ;
}
}
2011-12-22 16:05:19 +03:00
std : : set < CStack * > BattleInfo : : getAttackedCreatures ( const CSpell * s , int skillLevel , ui8 attackerOwner , BattleHex destinationTile )
2010-12-25 21:23:30 +02:00
{
std : : set < CStack * > attackedCres ; /*std::set to exclude multiple occurrences of two hex creatures*/
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
{
auto stack = getStackT ( 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 ) ;
}
}
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-05-18 23:50:16 +03:00
CStack * st = getStackT ( hex , onlyAlive ) ;
2010-12-25 21:23:30 +02:00
if ( st )
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 */
{
CStack * st = getStackT ( 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 */
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
{
CStack * st = getStackT ( destinationTile , onlyAlive ) ;
if ( st )
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-05-18 23:50:16 +03:00
CStack * st = getStackT ( hex , onlyAlive ) ;
2010-12-25 21:23:30 +02:00
if ( st )
attackedCres . insert ( st ) ;
}
}
return attackedCres ;
}
2011-12-22 16:05:19 +03:00
void BattleInfo : : getPotentiallyAttackableHexes ( AttackableTiles & at , const CStack * attacker , BattleHex destinationTile , BattleHex attackerPos )
2011-09-10 16:04:20 +03:00
{
2011-12-14 00:23:17 +03:00
const int WN = GameConstants : : BFIELD_WIDTH ;
2011-12-22 16:05:19 +03:00
ui16 hex = ( attackerPos ! = BattleHex : : INVALID ) ? attackerPos . hex : attacker - > position . hex ; //real or hypothetical (cursor) position
2011-07-04 22:34:49 +03:00
if ( attacker - > hasBonusOfType ( Bonus : : ATTACKS_ALL_ADJACENT ) )
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > hexes = attacker - > getSurroundingHexes ( attackerPos ) ;
BOOST_FOREACH ( BattleHex tile , hexes )
2011-07-04 22:34:49 +03:00
{
2011-09-10 16:04:20 +03:00
at . hostileCreaturePositions . insert ( tile ) ;
2011-07-04 22:34:49 +03:00
}
}
if ( attacker - > hasBonusOfType ( Bonus : : THREE_HEADED_ATTACK ) )
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > hexes = attacker - > getSurroundingHexes ( attackerPos ) ;
BOOST_FOREACH ( BattleHex tile , hexes )
2011-07-04 22:34:49 +03:00
{
2011-12-22 16:05:19 +03:00
if ( ( BattleHex : : mutualPosition ( tile , destinationTile ) > - 1 & & BattleHex : : mutualPosition ( tile , hex ) > - 1 ) //adjacent both to attacker's head and attacked tile
2011-09-05 21:12:46 +03:00
| | tile = = destinationTile ) //or simply attacked directly
2011-07-04 22:34:49 +03:00
{
2011-09-10 16:04:20 +03:00
CStack * st = getStackT ( tile , true ) ;
2011-09-05 21:12:46 +03:00
if ( st & & st - > owner ! = attacker - > owner ) //only hostile stacks - does it work well with Berserk?
{
2011-09-10 16:04:20 +03:00
at . hostileCreaturePositions . insert ( tile ) ;
2011-09-05 21:12:46 +03:00
}
2011-07-04 22:34:49 +03:00
}
}
}
if ( attacker - > hasBonusOfType ( Bonus : : TWO_HEX_ATTACK_BREATH ) )
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > hexes ; //only one, in fact
2011-07-04 22:34:49 +03:00
int pseudoVector = destinationTile . hex - hex ;
switch ( pseudoVector )
{
case 1 :
case - 1 :
2011-12-22 16:05:19 +03:00
BattleHex : : checkAndPush ( destinationTile . hex + pseudoVector , hexes ) ;
2011-07-04 22:34:49 +03:00
break ;
case WN : //17
case WN + 1 : //18
case - WN : //-17
case - WN + 1 : //-16
2011-12-22 16:05:19 +03:00
BattleHex : : checkAndPush ( destinationTile . hex + pseudoVector + ( ( hex / WN ) % 2 ? 1 : - 1 ) , hexes ) ;
2011-07-04 22:34:49 +03:00
break ;
case WN - 1 : //16
case - WN - 1 : //-18
2011-12-22 16:05:19 +03:00
BattleHex : : checkAndPush ( destinationTile . hex + pseudoVector + ( ( hex / WN ) % 2 ? 1 : 0 ) , hexes ) ;
2011-07-04 22:34:49 +03:00
break ;
}
2011-12-22 16:05:19 +03:00
BOOST_FOREACH ( BattleHex tile , hexes )
2011-07-04 22:34:49 +03:00
{
2011-09-10 16:04:20 +03:00
CStack * st = getStackT ( tile , true ) ;
2011-07-04 22:34:49 +03:00
if ( st ) //friendly stacks can also be damaged by Dragon Breath
{
2011-09-10 16:04:20 +03:00
at . friendlyCreaturePositions . insert ( tile ) ;
2011-07-04 22:34:49 +03:00
}
}
}
2011-09-10 16:04:20 +03:00
}
2011-12-22 16:05:19 +03:00
std : : set < CStack * > BattleInfo : : getAttackedCreatures ( const CStack * attacker , BattleHex destinationTile , BattleHex attackerPos )
2011-09-10 16:04:20 +03:00
{ //TODO: caching?
AttackableTiles at ;
getPotentiallyAttackableHexes ( at , attacker , destinationTile , attackerPos ) ;
std : : set < CStack * > attackedCres ;
2011-12-22 16:05:19 +03:00
BOOST_FOREACH ( BattleHex tile , at . hostileCreaturePositions ) //all around & three-headed attack
2011-09-10 16:04:20 +03:00
{
CStack * st = getStackT ( tile , true ) ;
if ( st & & st - > owner ! = attacker - > owner ) //only hostile stacks - does it work well with Berserk?
{
attackedCres . insert ( st ) ;
}
}
2011-12-22 16:05:19 +03:00
BOOST_FOREACH ( BattleHex tile , at . friendlyCreaturePositions )
2011-09-10 16:04:20 +03:00
{
CStack * st = getStackT ( tile , true ) ;
if ( st ) //friendly stacks can also be damaged by Dragon Breath
{
attackedCres . insert ( st ) ;
}
}
2011-07-04 22:34:49 +03:00
return attackedCres ;
}
2010-12-25 21:23:30 +02:00
2011-12-22 16:05:19 +03:00
std : : set < BattleHex > BattleInfo : : getAttackedHexes ( const CStack * attacker , BattleHex destinationTile , BattleHex attackerPos )
2011-09-10 16:04:20 +03:00
{
AttackableTiles at ;
getPotentiallyAttackableHexes ( at , attacker , destinationTile , attackerPos ) ;
2011-12-22 16:05:19 +03:00
std : : set < BattleHex > attackedHexes ;
BOOST_FOREACH ( BattleHex tile , at . hostileCreaturePositions )
2011-09-10 16:04:20 +03:00
{
2011-09-10 18:06:48 +03:00
CStack * st = getStackT ( tile , true ) ;
2011-09-10 16:04:20 +03:00
if ( st & & st - > owner ! = attacker - > owner ) //only hostile stacks - does it work well with Berserk?
{
attackedHexes . insert ( tile ) ;
}
}
2011-12-22 16:05:19 +03:00
BOOST_FOREACH ( BattleHex tile , at . friendlyCreaturePositions )
2011-09-10 16:04:20 +03:00
{
2011-09-10 18:06:48 +03:00
CStack * st = getStackT ( tile , true ) ;
2011-09-10 16:04:20 +03:00
if ( st ) //friendly stacks can also be damaged by Dragon Breath
{
attackedHexes . insert ( tile ) ;
}
}
return attackedHexes ;
}
2011-11-08 11:54:32 +03:00
std : : set < CStack * > BattleInfo : : getAdjacentCreatures ( const CStack * stack ) const
2011-10-17 11:24:51 +03:00
{
std : : set < CStack * > stacks ;
CStack * localStack ;
2011-12-22 16:05:19 +03:00
BOOST_FOREACH ( BattleHex hex , stack - > getSurroundingHexes ( ) )
2011-10-17 11:24:51 +03:00
{
2011-11-08 11:54:32 +03:00
localStack = const_cast < CStack * > ( getStackT ( hex , true ) ) ; //only alive?
2011-10-17 11:24:51 +03:00
if ( localStack )
stacks . insert ( localStack ) ;
}
return stacks ;
}
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 ;
}
ui32 BattleInfo : : getSpellCost ( const CSpell * sp , const CGHeroInstance * caster ) const
{
ui32 ret = caster - > getSpellCost ( sp ) ;
//checking for friendly stacks reducing cost of the spell and
//enemy stacks increasing it
si32 manaReduction = 0 ;
si32 manaIncrease = 0 ;
for ( int g = 0 ; g < stacks . size ( ) ; + + g )
{
if ( stacks [ g ] - > owner = = caster - > tempOwner & & stacks [ g ] - > hasBonusOfType ( Bonus : : CHANGES_SPELL_COST_FOR_ALLY ) )
{
2011-12-14 00:23:17 +03:00
vstd : : amax ( manaReduction , stacks [ g ] - > valOfBonuses ( Bonus : : CHANGES_SPELL_COST_FOR_ALLY ) ) ;
2010-12-25 21:23:30 +02:00
}
if ( stacks [ g ] - > owner ! = caster - > tempOwner & & stacks [ g ] - > hasBonusOfType ( Bonus : : CHANGES_SPELL_COST_FOR_ENEMY ) )
{
2011-12-14 00:23:17 +03:00
vstd : : amax ( manaIncrease , stacks [ g ] - > valOfBonuses ( Bonus : : CHANGES_SPELL_COST_FOR_ENEMY ) ) ;
2010-12-25 21:23:30 +02:00
}
}
2011-10-20 20:41:40 +03:00
return ret - manaReduction + manaIncrease ;
2010-12-25 21:23:30 +02:00
}
2011-12-22 16:05:19 +03:00
int BattleInfo : : hexToWallPart ( BattleHex hex ) const
2010-12-25 21:23:30 +02:00
{
if ( siege = = 0 ) //there is no battle!
return - 1 ;
static const std : : pair < int , int > attackable [ ] = //potentially attackable parts of wall
{ std : : make_pair ( 50 , 0 ) , std : : make_pair ( 183 , 1 ) , std : : make_pair ( 182 , 2 ) , std : : make_pair ( 130 , 3 ) ,
2011-10-20 20:41:40 +03:00
std : : make_pair ( 62 , 4 ) , std : : make_pair ( 29 , 5 ) , std : : make_pair ( 12 , 6 ) , std : : make_pair ( 95 , 7 ) , std : : make_pair ( 96 , 7 ) ,
std : : make_pair ( 45 , - 2 ) , std : : make_pair ( 78 , - 2 ) , std : : make_pair ( 112 , - 2 ) , std : : make_pair ( 147 , - 2 ) } ; // -2 - indestructible walls
2010-12-25 21:23:30 +02:00
for ( int g = 0 ; g < ARRAY_COUNT ( attackable ) ; + + g )
{
if ( attackable [ g ] . first = = hex )
return attackable [ g ] . second ;
}
return - 1 ; //not found!
}
int BattleInfo : : lineToWallHex ( int line ) const
{
static const int lineToHex [ ] = { 12 , 29 , 45 , 62 , 78 , 95 , 112 , 130 , 147 , 165 , 182 } ;
return lineToHex [ line ] ;
}
2011-12-22 16:05:19 +03:00
std : : pair < const CStack * , BattleHex > BattleInfo : : getNearestStack ( const CStack * closest , boost : : logic : : tribool attackerOwned ) const
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
bool ac [ GameConstants : : BFIELD_SIZE ] ;
2011-12-22 16:05:19 +03:00
std : : set < BattleHex > occupyable ;
2010-12-25 21:23:30 +02:00
2011-01-07 12:48:31 +02:00
getAccessibilityMap ( ac , closest - > doubleWide ( ) , closest - > attackerOwned , false , occupyable , closest - > hasBonusOfType ( Bonus : : FLYING ) , closest ) ;
2010-12-25 21:23:30 +02:00
2011-12-22 16:05:19 +03:00
BattleHex predecessor [ GameConstants : : BFIELD_SIZE ] ;
2011-12-14 00:23:17 +03:00
int dist [ GameConstants : : BFIELD_SIZE ] ;
2010-12-25 21:23:30 +02:00
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>
2011-12-14 00:23:17 +03:00
for ( int g = 0 ; g < GameConstants : : BFIELD_SIZE ; + + g )
2010-12-25 21:23:30 +02:00
{
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 ] ) ;
}
2011-12-22 16:05:19 +03:00
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 ) ] ;
2011-07-08 16:17:05 +03:00
healedHealth = calculateSpellBonus ( healedHealth , spell , caster , stack ) ;
return std : : min < ui32 > ( healedHealth , stack - > MaxHealth ( ) - stack - > firstHPleft + ( resurrect ? stack - > baseAmount * stack - > MaxHealth ( ) : 0 ) ) ;
}
ui32 BattleInfo : : calculateHealedHP ( int healedHealth , const CSpell * spell , const CStack * stack ) const
{
bool resurrect = resurrects ( spell - > id ) ;
return std : : min < ui32 > ( healedHealth , stack - > MaxHealth ( ) - stack - > firstHPleft + ( resurrect ? stack - > baseAmount * stack - > MaxHealth ( ) : 0 ) ) ;
}
ui32 BattleInfo : : calculateHealedHP ( const CSpell * spell , int usedSpellPower , int spellSchoolLevel , const CStack * stack ) const
{
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
{
2011-10-03 22:29:36 +03:00
return vstd : : contains ( VLC - > spellh - > risingSpells , spellid ) ;
2010-12-25 21:23:30 +02:00
}
void BattleInfo : : getStackQueue ( std : : vector < const CStack * > & out , int howMany , int turn /*= 0*/ , int lastMoved /*= -1*/ ) const
{
//we'll split creatures with remaining movement to 4 parts
std : : vector < const CStack * > phase [ 4 ] ; //0 - turrets/catapult, 1 - normal (unmoved) creatures, other war machines, 2 - waited cres that had morale, 3 - rest of waited cres
int toMove = 0 ; //how many stacks still has move
const CStack * active = getStack ( activeStack ) ;
//active stack hasn't taken any action yet - must be placed at the beginning of queue, no matter what
2011-12-14 00:23:17 +03:00
if ( ! turn & & active & & active - > willMove ( ) & & ! vstd : : contains ( active - > state , EBattleStackState : : WAITING ) )
2010-12-25 21:23:30 +02:00
{
out . push_back ( active ) ;
if ( out . size ( ) = = howMany )
return ;
}
2011-12-14 00:23:17 +03:00
for ( ui32 i = 0 ; i < stacks . size ( ) ; + + i )
2010-12-25 21:23:30 +02:00
{
const CStack * const s = stacks [ i ] ;
2011-05-30 22:20:14 +03:00
if ( ( turn < = 0 & & ! s - > willMove ( ) ) //we are considering current round and stack won't move
| | ( turn > 0 & & ! s - > canMove ( turn ) ) //stack won't be able to move in later rounds
| | ( turn < = 0 & & s = = active & & out . size ( ) & & s = = out . front ( ) ) ) //it's active stack already added at the beginning of queue
2010-12-25 21:23:30 +02:00
{
continue ;
}
int p = - 1 ; //in which phase this tack will move?
2011-12-14 00:23:17 +03:00
if ( turn < = 0 & & vstd : : contains ( s - > state , EBattleStackState : : WAITING ) ) //consider waiting state only for ongoing round
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
if ( vstd : : contains ( s - > state , EBattleStackState : : HAD_MORALE ) )
2010-12-25 21:23:30 +02:00
p = 2 ;
else
p = 3 ;
}
else if ( s - > getCreature ( ) - > idNumber = = 145 | | s - > getCreature ( ) - > idNumber = = 149 ) //catapult and turrets are first
{
p = 0 ;
}
else
{
p = 1 ;
}
phase [ p ] . push_back ( s ) ;
toMove + + ;
}
for ( int i = 0 ; i < 4 ; i + + )
std : : sort ( phase [ i ] . begin ( ) , phase [ i ] . end ( ) , CMP_stack ( i , turn > 0 ? turn : 0 ) ) ;
for ( size_t i = 0 ; i < phase [ 0 ] . size ( ) & & i < howMany ; i + + )
out . push_back ( phase [ 0 ] [ i ] ) ;
if ( out . size ( ) = = howMany )
return ;
if ( lastMoved = = - 1 )
{
if ( active )
{
if ( out . size ( ) & & out . front ( ) = = active )
lastMoved = active - > attackerOwned ;
else
lastMoved = active - > attackerOwned ;
}
else
{
lastMoved = 0 ;
}
}
int pi = 1 ;
while ( out . size ( ) < howMany )
{
const CStack * hlp = takeStack ( phase [ pi ] , lastMoved , turn ) ;
if ( ! hlp )
{
pi + + ;
if ( pi > 3 )
{
//if(turn != 2)
getStackQueue ( out , howMany , turn + 1 , lastMoved ) ;
return ;
}
}
else
{
out . push_back ( hlp ) ;
}
}
}
2011-12-22 16:05:19 +03:00
si8 BattleInfo : : hasDistancePenalty ( const CStack * stack , BattleHex destHex ) const
2010-12-25 21:23:30 +02:00
{
struct HLP
{
2011-12-22 16:05:19 +03:00
static bool lowerAnalyze ( const CStack * stack , BattleHex hex )
2010-12-25 21:23:30 +02:00
{
2011-12-22 16:05:19 +03:00
int distance = BattleHex : : getDistance ( hex , stack - > position ) ;
2010-12-25 21:23:30 +02:00
//I hope it's approximately correct
return distance > 10 & & ! stack - > hasBonusOfType ( Bonus : : NO_DISTANCE_PENALTY ) ;
}
} ;
const CStack * dstStack = getStackT ( destHex , false ) ;
if ( dstStack - > doubleWide ( ) )
return HLP : : lowerAnalyze ( stack , destHex ) & & HLP : : lowerAnalyze ( stack , dstStack - > occupiedHex ( ) ) ;
else
return HLP : : lowerAnalyze ( stack , destHex ) ;
}
2011-01-08 20:33:40 +02:00
si8 BattleInfo : : sameSideOfWall ( int pos1 , int pos2 ) const
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
int wallInStackLine = lineToWallHex ( pos1 / GameConstants : : BFIELD_WIDTH ) ;
int wallInDestLine = lineToWallHex ( pos2 / GameConstants : : BFIELD_WIDTH ) ;
2010-12-25 21:23:30 +02:00
bool stackLeft = pos1 < wallInStackLine ;
bool destLeft = pos2 < wallInDestLine ;
return stackLeft ! = destLeft ;
}
2011-12-22 16:05:19 +03:00
si8 BattleInfo : : hasWallPenalty ( const CStack * stack , BattleHex destHex ) const
2010-12-25 21:23:30 +02:00
{
2011-10-20 20:41:40 +03:00
if ( ! siege | | stack - > hasBonusOfType ( Bonus : : NO_WALL_PENALTY ) )
2010-12-25 21:23:30 +02:00
{
return false ;
}
2011-10-20 20:41:40 +03:00
2011-12-14 00:23:17 +03:00
int wallInStackLine = lineToWallHex ( stack - > position / GameConstants : : BFIELD_WIDTH ) ;
int wallInDestLine = lineToWallHex ( destHex / GameConstants : : BFIELD_WIDTH ) ;
2011-10-20 20:41:40 +03:00
bool stackLeft = stack - > position < wallInStackLine ;
bool destRight = destHex > wallInDestLine ;
if ( stackLeft & & destRight ) //shooting from outside to inside
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
int row = ( stack - > position + destHex ) / ( 2 * GameConstants : : BFIELD_WIDTH ) ;
2012-02-18 20:39:47 +03:00
if ( stack - > position > destHex & & ( ( destHex % GameConstants : : BFIELD_WIDTH - stack - > position % GameConstants : : BFIELD_WIDTH ) < 2 ) ) //shooting up high
2011-10-20 20:41:40 +03:00
row - = 2 ;
int wallPos = lineToWallHex ( row ) ;
if ( hexToWallPart ( wallPos ) ! = - 1 ) //wall still exists or is indestructible
return true ;
2010-12-25 21:23:30 +02:00
}
2011-10-20 20:41:40 +03:00
return false ;
2010-12-25 21:23:30 +02:00
}
2011-12-22 16:05:19 +03:00
si8 BattleInfo : : canTeleportTo ( const CStack * stack , BattleHex destHex , int telportLevel ) const
2010-12-25 21:23:30 +02:00
{
2011-12-14 00:23:17 +03:00
bool ac [ GameConstants : : BFIELD_SIZE ] ;
2010-12-25 21:23:30 +02:00
2011-12-22 16:05:19 +03:00
std : : set < BattleHex > occupyable ;
2010-12-25 21:23:30 +02:00
2011-01-07 12:48:31 +02:00
getAccessibilityMap ( ac , stack - > doubleWide ( ) , stack - > attackerOwned , false , occupyable , stack - > hasBonusOfType ( Bonus : : FLYING ) , stack ) ;
2010-12-25 21:23:30 +02:00
if ( siege & & telportLevel < 2 ) //check for wall
{
2011-01-07 12:48:31 +02:00
return ac [ destHex ] & & sameSideOfWall ( stack - > position , destHex ) ;
2010-12-25 21:23:30 +02:00
}
else
{
return ac [ destHex ] ;
}
}
2011-12-22 16:05:19 +03:00
bool BattleInfo : : battleCanShoot ( const CStack * stack , BattleHex dest ) const
2011-01-07 12:48:31 +02:00
{
2012-04-01 15:35:33 +03:00
if ( tacticDistance ) //no shooting during tactics
return false ;
2011-01-07 12:48:31 +02:00
const CStack * dst = getStackT ( dest ) ;
if ( ! stack | | ! dst ) return false ;
const CGHeroInstance * stackHero = battleGetOwner ( stack ) ;
if ( stack - > hasBonusOfType ( Bonus : : FORGETFULL ) ) //forgetfulness
return false ;
if ( stack - > getCreature ( ) - > idNumber = = 145 & & dst ) //catapult cannot attack creatures
return false ;
if ( stack - > hasBonusOfType ( Bonus : : SHOOTER ) //it's shooter
& & stack - > owner ! = dst - > owner
& & dst - > alive ( )
& & ( ! isStackBlocked ( stack ) | | NBonus : : hasOfType ( stackHero , Bonus : : FREE_SHOOTING ) )
& & stack - > shots
)
return true ;
return false ;
}
2011-01-08 20:33:40 +02:00
bool BattleInfo : : battleCanFlee ( int player ) const
2010-12-25 21:23:30 +02:00
{
2011-01-09 19:41:46 +02:00
if ( player = = sides [ 0 ] )
2011-01-07 12:48:31 +02:00
{
if ( ! heroes [ 0 ] )
return false ; //current player have no hero
}
else
{
if ( ! heroes [ 1 ] )
return false ;
}
if ( ( heroes [ 0 ] & & heroes [ 0 ] - > hasBonusOfType ( Bonus : : ENEMY_CANT_ESCAPE ) ) //eg. one of heroes is wearing shakles of war
| | ( heroes [ 1 ] & & heroes [ 1 ] - > hasBonusOfType ( Bonus : : ENEMY_CANT_ESCAPE ) ) )
return false ;
2011-01-09 19:41:46 +02:00
if ( player = = sides [ 1 ] & & siege //defender in siege
2011-01-07 12:48:31 +02:00
& & ! ( town - > subID = = 6 & & vstd : : contains ( town - > builtBuildings , 17 ) ) ) //without escape tunnel
return false ;
return true ;
}
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 ] ;
}
2011-02-21 18:53:23 +02:00
si8 BattleInfo : : battleMinSpellLevel ( ) const
2011-01-07 12:48:31 +02:00
{
2011-02-21 18:53:23 +02:00
si8 levelLimit = 0 ;
2011-01-07 12:48:31 +02:00
2011-02-21 15:27:31 +02:00
if ( const CGHeroInstance * h1 = heroes [ 0 ] )
2011-01-07 12:48:31 +02:00
{
2011-12-14 00:23:17 +03:00
vstd : : amax ( levelLimit , h1 - > valOfBonuses ( Bonus : : LEVEL_SPELL_IMMUNITY ) ) ;
2011-01-07 12:48:31 +02:00
}
2011-02-21 15:27:31 +02:00
if ( const CGHeroInstance * h2 = heroes [ 1 ] )
2011-01-07 12:48:31 +02:00
{
2011-12-14 00:23:17 +03:00
vstd : : amax ( levelLimit , h2 - > valOfBonuses ( Bonus : : LEVEL_SPELL_IMMUNITY ) ) ;
2011-01-07 12:48:31 +02:00
}
return levelLimit ;
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 ;
2011-12-14 00:23:17 +03:00
const JsonNode config ( GameConstants : : DATA_DIR + " /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 )
{
CStack * stack = curB - > generateNewStack ( * heroes [ i ] - > commander , ! i , 255 ,
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-12-22 16:05:19 +03:00
bool BattleInfo : : isInTacticRange ( BattleHex dest ) const
2011-02-12 18:12:48 +02:00
{
2010-12-25 21:23:30 +02:00
2011-02-14 00:46:04 +02:00
return ( ( ! tacticsSide & & dest . getX ( ) > 0 & & dest . getX ( ) < = tacticDistance )
2011-12-14 00:23:17 +03:00
| | ( tacticsSide & & dest . getX ( ) < GameConstants : : BFIELD_WIDTH - 1 & & dest . getX ( ) > = GameConstants : : BFIELD_WIDTH - tacticDistance - 1 ) ) ;
2011-02-12 18:12:48 +02:00
}
2010-12-25 21:23:30 +02:00
2011-12-14 00:23:17 +03:00
ESpellCastProblem : : ESpellCastProblem BattleInfo : : battleCanCastSpell ( int player , ECastingMode : : ECastingMode mode ) const
2011-02-15 21:54:55 +02:00
{
int side = sides [ 0 ] = = player ? 0 : 1 ;
2011-02-20 20:32:39 +02:00
switch ( mode )
{
2011-12-14 00:23:17 +03:00
case ECastingMode : : HERO_CASTING :
2011-02-20 20:32:39 +02:00
{
2012-03-29 00:54:43 +03:00
if ( tacticDistance )
return ESpellCastProblem : : ONGOING_TACTIC_PHASE ;
2011-02-20 20:32:39 +02:00
if ( castSpells [ side ] > 0 )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : ALREADY_CASTED_THIS_TURN ;
2011-02-20 20:32:39 +02:00
if ( ! heroes [ side ] )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : NO_HERO_TO_CAST_SPELL ;
2011-02-20 20:32:39 +02:00
if ( ! heroes [ side ] - > getArt ( 17 ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : NO_SPELLBOOK ;
2011-02-20 20:32:39 +02:00
}
break ;
}
2011-02-15 21:54:55 +02:00
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : OK ;
2011-02-15 21:54:55 +02:00
}
2011-12-14 00:23:17 +03:00
ESpellCastProblem : : ESpellCastProblem BattleInfo : : battleCanCastThisSpell ( int player , const CSpell * spell , ECastingMode : : ECastingMode mode ) const
2011-02-15 21:54:55 +02:00
{
2011-12-14 00:23:17 +03:00
ESpellCastProblem : : ESpellCastProblem genProblem = battleCanCastSpell ( player , mode ) ;
if ( genProblem ! = ESpellCastProblem : : OK )
2011-02-15 21:54:55 +02:00
return genProblem ;
2011-02-20 20:32:39 +02:00
2011-02-15 21:54:55 +02:00
int cside = sides [ 0 ] = = player ? 0 : 1 ; //caster's side
2011-02-20 20:32:39 +02:00
switch ( mode )
{
2011-12-14 00:23:17 +03:00
case ECastingMode : : HERO_CASTING :
2011-02-20 20:32:39 +02:00
{
const CGHeroInstance * caster = heroes [ cside ] ;
if ( ! caster - > canCastThisSpell ( spell ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : HERO_DOESNT_KNOW_SPELL ;
2011-02-15 21:54:55 +02:00
2011-02-20 20:32:39 +02:00
if ( caster - > mana < getSpellCost ( spell , caster ) ) //not enough mana
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : NOT_ENOUGH_MANA ;
2011-02-20 20:32:39 +02:00
}
break ;
}
2011-02-15 21:54:55 +02:00
if ( spell - > id < 10 ) //it's adventure spell (not combat))
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL ;
2011-02-15 21:54:55 +02:00
if ( NBonus : : hasOfType ( heroes [ 1 - cside ] , Bonus : : SPELL_IMMUNITY , spell - > id ) ) //non - casting hero provides immunity for this spell
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : SECOND_HEROS_SPELL_IMMUNITY ;
2011-02-15 21:54:55 +02:00
2011-02-21 18:53:23 +02:00
if ( battleMinSpellLevel ( ) > spell - > level ) //non - casting hero stops caster from casting this spell
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : SPELL_LEVEL_LIMIT_EXCEEDED ;
2011-02-15 21:54:55 +02:00
int spellIDs [ ] = { 66 , 67 , 68 , 69 } ; //IDs of summon elemental spells (fire, earth, water, air)
int creIDs [ ] = { 114 , 113 , 115 , 112 } ; //(fire, earth, water, air)
int * idp = std : : find ( spellIDs , spellIDs + ARRAY_COUNT ( spellIDs ) , spell - > id ) ;
int arpos = idp - spellIDs ;
if ( arpos < ARRAY_COUNT ( spellIDs ) )
{
//check if there are summoned elementals of other type
BOOST_FOREACH ( const CStack * st , stacks )
{
2011-12-14 00:23:17 +03:00
if ( vstd : : contains ( st - > state , EBattleStackState : : SUMMONED ) & & st - > getCreature ( ) - > idNumber ! = creIDs [ arpos ] )
2011-02-15 21:54:55 +02:00
{
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : ANOTHER_ELEMENTAL_SUMMONED ;
2011-02-15 21:54:55 +02:00
}
}
}
2011-02-21 18:53:23 +02:00
//checking if there exists an appropriate target
switch ( spell - > getTargetType ( ) )
{
case CSpell : : CREATURE :
case CSpell : : CREATURE_EXPERT_MASSIVE :
2011-12-14 00:23:17 +03:00
if ( mode = = ECastingMode : : HERO_CASTING )
2011-02-21 18:53:23 +02:00
{
const CGHeroInstance * caster = getHero ( player ) ;
bool targetExists = false ;
BOOST_FOREACH ( const CStack * stack , stacks )
{
switch ( spell - > positiveness )
{
2012-02-17 00:19:07 +03:00
case CSpell : : POSITIVE :
2011-02-21 18:53:23 +02:00
if ( stack - > owner = = caster - > getOwner ( ) )
{
2011-12-14 00:23:17 +03:00
if ( battleIsImmune ( caster , spell , mode , stack - > position ) = = ESpellCastProblem : : OK )
2011-02-21 18:53:23 +02:00
{
targetExists = true ;
break ;
}
}
break ;
2012-02-17 00:19:07 +03:00
case CSpell : : NEUTRAL :
2011-12-14 00:23:17 +03:00
if ( battleIsImmune ( caster , spell , mode , stack - > position ) = = ESpellCastProblem : : OK )
2011-02-21 18:53:23 +02:00
{
targetExists = true ;
break ;
}
break ;
2012-02-17 00:19:07 +03:00
case CSpell : : NEGATIVE :
2011-02-21 18:53:23 +02:00
if ( stack - > owner ! = caster - > getOwner ( ) )
{
2011-12-14 00:23:17 +03:00
if ( battleIsImmune ( caster , spell , mode , stack - > position ) = = ESpellCastProblem : : OK )
2011-02-21 18:53:23 +02:00
{
targetExists = true ;
break ;
}
}
break ;
}
}
if ( ! targetExists )
{
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
2011-02-21 18:53:23 +02:00
}
}
break ;
case CSpell : : OBSTACLE :
break ;
}
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : OK ;
2011-02-15 21:54:55 +02:00
}
2011-12-22 16:05:19 +03:00
ESpellCastProblem : : ESpellCastProblem BattleInfo : : battleCanCastThisSpellHere ( int player , const CSpell * spell , ECastingMode : : ECastingMode mode , BattleHex dest ) const
2011-02-20 20:32:39 +02:00
{
2011-12-14 00:23:17 +03:00
ESpellCastProblem : : ESpellCastProblem moreGeneralProblem = battleCanCastThisSpell ( player , spell , mode ) ;
if ( moreGeneralProblem ! = ESpellCastProblem : : OK )
2011-02-20 20:32:39 +02:00
return moreGeneralProblem ;
2012-05-18 23:50:16 +03:00
if ( spell - > getTargetType ( ) = = CSpell : : OBSTACLE )
{
//isObstacleOnTile(dest)
//
//
//TODO
//assert that it's remove obstacle
//rules whether we can remove spell-created obstacle
2012-03-31 00:36:07 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
2012-05-18 23:50:16 +03:00
}
2012-03-31 00:36:07 +03:00
//get dead stack if we cast resurrection or animate dead
2012-04-08 04:43:40 +03:00
const CStack * deadStack = getStackIf ( [ dest ] ( const CStack * s ) { return ! s - > alive ( ) & & s - > position = = dest ; } ) ;
2012-04-28 22:40:27 +03:00
const CStack * aliveStack = getStackIf ( [ dest ] ( const CStack * s ) { return s - > alive ( ) & & s - > position = = dest ; } ) ;
2012-04-08 04:43:40 +03:00
2012-03-31 00:36:07 +03:00
if ( spell - > isRisingSpell ( ) )
{
2012-04-28 22:40:27 +03:00
if ( ! deadStack & & ! aliveStack )
2012-03-31 00:36:07 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
2012-05-05 00:16:39 +03:00
if ( spell - > id = = Spells : : ANIMATE_DEAD & & deadStack & & ! deadStack - > hasBonusOfType ( Bonus : : UNDEAD ) )
2012-04-08 04:26:33 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
2012-04-28 22:40:27 +03:00
if ( deadStack & & deadStack - > owner ! = player ) //you can resurrect only your own stacks //FIXME: it includes alive stacks as well
2012-03-31 00:36:07 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
}
2012-04-03 02:23:14 +03:00
else if ( spell - > getTargetType ( ) = = CSpell : : CREATURE | | spell - > getTargetType ( ) = = CSpell : : CREATURE_EXPERT_MASSIVE )
2012-03-31 00:36:07 +03:00
{
2012-04-08 04:43:40 +03:00
if ( ! aliveStack )
2012-03-31 00:36:07 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
2012-04-08 04:43:40 +03:00
if ( spell - > isNegative ( ) & & aliveStack - > owner = = player )
2012-03-31 00:36:07 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
2012-04-08 04:43:40 +03:00
if ( spell - > isPositive ( ) & & aliveStack - > owner ! = player )
2012-03-31 00:36:07 +03:00
return ESpellCastProblem : : NO_APPROPRIATE_TARGET ;
}
2011-12-14 00:23:17 +03:00
if ( mode ! = ECastingMode : : CREATURE_ACTIVE_CASTING & & mode ! = ECastingMode : : ENCHANTER_CASTING )
2011-10-01 22:56:54 +03:00
return battleIsImmune ( getHero ( player ) , spell , mode , dest ) ;
else
return battleIsImmune ( NULL , spell , mode , dest ) ;
2011-02-21 18:53:23 +02:00
}
2011-10-03 22:29:36 +03:00
TSpell BattleInfo : : getRandomBeneficialSpell ( const CStack * subject ) const
{
std : : vector < TSpell > possibleSpells ;
2011-10-04 16:12:29 +03:00
CSpell * spell ;
2011-12-14 00:23:17 +03:00
for ( int i = 0 ; i < GameConstants : : SPELLS_QUANTITY ; + + i ) //should not use future spells added by mods
2011-10-03 22:29:36 +03:00
{
2011-10-04 16:12:29 +03:00
spell = VLC - > spellh - > spells [ i ] ;
2012-02-17 00:19:07 +03:00
if ( spell - > isPositive ( ) ) //only positive
2011-10-03 22:29:36 +03:00
{
2011-10-04 16:12:29 +03:00
if ( subject - > hasBonusFrom ( Bonus : : SPELL_EFFECT , i ) | |
2011-12-14 00:23:17 +03:00
battleCanCastThisSpellHere ( subject - > owner , spell , ECastingMode : : CREATURE_ACTIVE_CASTING , subject - > position ) ! = ESpellCastProblem : : OK )
2011-10-03 22:29:36 +03:00
continue ;
switch ( i )
{
2011-11-13 12:29:22 +03:00
case Spells : : SHIELD :
case Spells : : FIRE_SHIELD : // not if all enemy units are shooters
2011-10-03 22:29:36 +03:00
{
bool walkerPresent = false ;
BOOST_FOREACH ( CStack * stack , stacks )
{
if ( ( stack - > owner ! = subject - > owner ) & & ! stack - > shots )
{
walkerPresent = true ;
break ;
}
}
if ( ! walkerPresent )
continue ;
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : AIR_SHIELD : //only against active shooters
2011-10-03 22:29:36 +03:00
{
bool shooterPresent = false ;
BOOST_FOREACH ( CStack * stack , stacks )
{
if ( ( stack - > owner ! = subject - > owner ) & & stack - > hasBonusOfType ( Bonus : : SHOOTER ) & & stack - > shots )
{
shooterPresent = true ;
break ;
}
}
if ( ! shooterPresent )
continue ;
break ;
}
2011-11-13 12:29:22 +03:00
case Spells : : ANTI_MAGIC :
case Spells : : MAGIC_MIRROR :
2011-10-03 22:29:36 +03:00
{
if ( ! heroes [ whatSide ( theOtherPlayer ( subject - > owner ) ) ] ) //only if there is enemy hero
continue ;
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : CURE : //only damaged units - what about affected by curse?
2011-10-03 22:29:36 +03:00
{
if ( subject - > firstHPleft > = subject - > MaxHealth ( ) )
continue ;
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : BLOODLUST :
2011-10-03 22:29:36 +03:00
{
if ( subject - > shots ) //if can shoot - only if enemy uits are adjacent
continue ;
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : PRECISION :
2011-10-03 22:29:36 +03:00
{
if ( ! ( subject - > hasBonusOfType ( Bonus : : SHOOTER ) & & subject - > shots ) )
continue ;
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : SLAYER : //only if monsters are present
2011-10-03 22:29:36 +03:00
{
bool monsterPresent = false ;
BOOST_FOREACH ( CStack * stack , stacks )
{
if ( ( stack - > owner ! = subject - > owner ) & &
( stack - > hasBonus ( Selector : : type ( Bonus : : KING1 ) | | Selector : : type ( Bonus : : KING2 ) | | Selector : : type ( Bonus : : KING3 ) ) ) )
{
monsterPresent = true ;
break ;
}
}
if ( ! monsterPresent )
continue ;
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : CLONE : //not allowed
2011-10-08 12:11:36 +03:00
continue ;
break ;
2011-10-03 22:29:36 +03:00
}
possibleSpells . push_back ( i ) ;
}
}
if ( possibleSpells . size ( ) )
return possibleSpells [ ran ( ) % possibleSpells . size ( ) ] ;
else
return - 1 ;
}
2011-10-04 16:12:29 +03:00
TSpell BattleInfo : : getRandomCastedSpell ( const CStack * caster ) const
{
TBonusListPtr bl = caster - > getBonuses ( Selector : : type ( Bonus : : SPELLCASTER ) ) ;
2011-10-08 09:59:36 +03:00
if ( ! bl - > size ( ) )
return - 1 ;
int totalWeight = 0 ;
2011-10-04 16:12:29 +03:00
BOOST_FOREACH ( Bonus * b , * bl )
{
totalWeight + = std : : max ( b - > additionalInfo , 1 ) ; //minimal chance to cast is 1
}
int randomPos = ran ( ) % totalWeight ;
BOOST_FOREACH ( Bonus * b , * bl )
{
2011-10-08 09:59:36 +03:00
randomPos - = std : : max ( b - > additionalInfo , 1 ) ;
2011-10-04 16:12:29 +03:00
if ( randomPos < 0 )
{
return b - > subtype ;
}
}
return - 1 ;
}
2011-10-03 22:29:36 +03: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 ] ;
}
2011-02-28 17:14:26 +02:00
bool NegateRemover ( const Bonus * b )
{
return b - > source = = Bonus : : CREATURE_ABILITY ;
}
2011-06-25 09:55:35 +03:00
bool BattleInfo : : battleTestElementalImmunity ( const CStack * subject , const CSpell * spell , Bonus : : BonusType element , bool damageSpell ) const //helper for battleisImmune
{
2012-02-17 00:19:07 +03:00
if ( ! spell - > isPositive ( ) ) //negative or indifferent
2011-06-25 09:55:35 +03:00
{
2011-06-25 18:05:01 +03:00
if ( ( damageSpell & & subject - > hasBonusOfType ( element , 2 ) ) | | subject - > hasBonusOfType ( element , 1 ) )
2011-06-25 09:55:35 +03:00
return true ;
}
2012-02-17 00:19:07 +03:00
else if ( spell - > isPositive ( ) ) //positive
2011-06-25 09:55:35 +03:00
{
if ( subject - > hasBonusOfType ( element , 0 ) ) //must be immune to all spells
return true ;
}
return false ;
}
2011-12-22 16:05:19 +03:00
ESpellCastProblem : : ESpellCastProblem BattleInfo : : battleIsImmune ( const CGHeroInstance * caster , const CSpell * spell , ECastingMode : : ECastingMode mode , BattleHex dest ) const
2011-02-21 18:53:23 +02:00
{
2012-02-29 16:47:57 +03:00
const CStack * subject ;
bool risingSpell = vstd : : contains ( VLC - > spellh - > risingSpells , spell - > id ) ;
if ( risingSpell )
subject = getStackT ( dest , false ) ; //including dead stacks
else
subject = getStackT ( dest , true ) ; //only alive
2011-02-21 18:53:23 +02:00
if ( subject )
2011-02-20 20:32:39 +02:00
{
2012-02-17 00:19:07 +03:00
if ( spell - > isPositive ( ) & & subject - > hasBonusOfType ( Bonus : : RECEPTIVE ) ) //accept all positive spells
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : OK ;
2011-05-13 20:51:12 +03:00
2011-07-15 19:41:43 +03:00
switch ( spell - > id ) //TODO: more general logic for new spells?
{
2011-11-13 12:29:22 +03:00
case Spells : : DESTROY_UNDEAD :
2011-07-15 19:41:43 +03:00
if ( ! subject - > hasBonusOfType ( Bonus : : UNDEAD ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-07-15 19:41:43 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : DEATH_RIPPLE :
2011-10-29 14:12:30 +03:00
if ( subject - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ; //don't break here - undeads and war machines are immune, non-living are not
2011-11-13 12:29:22 +03:00
case Spells : : BLESS :
case Spells : : CURSE : //undeads are immune to bless & curse
2011-07-15 19:41:43 +03:00
if ( subject - > hasBonusOfType ( Bonus : : UNDEAD ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-07-15 19:41:43 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : HASTE :
case Spells : : SLOW :
case Spells : : TELEPORT :
case Spells : : CLONE :
2011-10-29 14:12:30 +03:00
if ( subject - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) )
2012-02-20 12:50:49 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ; //war machines are immune to some spells than involve movement
if ( spell - > id = = Spells : : CLONE & & caster ) //TODO: how about stacks casting Clone?
2012-02-20 11:19:03 +03:00
{
2012-02-20 12:50:49 +03:00
if ( vstd : : contains ( subject - > state , EBattleStackState : : CLONED ) )
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ; //can't clone already cloned creature
2012-02-20 11:19:03 +03:00
int maxLevel = ( std : : max ( caster - > getSpellSchoolLevel ( spell ) , ( ui8 ) 1 ) + 4 ) ;
int creLevel = subject - > getCreature ( ) - > level ;
if ( maxLevel < creLevel ) //tier 1-5 for basic, 1-6 for advanced, 1-7 for expert
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
}
2011-10-29 14:12:30 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : FORGETFULNESS :
2011-07-15 19:41:43 +03:00
if ( ! subject - > hasBonusOfType ( Bonus : : SHOOTER ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-07-15 19:41:43 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : DISPEL_HELPFUL_SPELLS :
2011-07-15 19:41:43 +03:00
{
2011-09-06 16:59:26 +03:00
TBonusListPtr spellBon = subject - > getSpellBonuses ( ) ;
2011-07-15 19:41:43 +03:00
bool hasPositiveSpell = false ;
BOOST_FOREACH ( const Bonus * b , * spellBon )
{
2012-02-17 00:19:07 +03:00
if ( VLC - > spellh - > spells [ b - > sid ] - > isPositive ( ) )
2011-07-15 19:41:43 +03:00
{
hasPositiveSpell = true ;
break ;
}
}
if ( ! hasPositiveSpell )
{
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : NO_SPELLS_TO_DISPEL ;
2011-07-15 19:41:43 +03:00
}
}
break ;
}
2011-06-25 09:55:35 +03:00
2011-10-08 09:59:36 +03:00
bool damageSpell = ( vstd : : contains ( VLC - > spellh - > damageSpells , spell - > id ) ) ;
2011-06-25 09:55:35 +03:00
2011-06-25 18:05:01 +03:00
if ( damageSpell & & subject - > hasBonusOfType ( Bonus : : DIRECT_DAMAGE_IMMUNITY ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-06-18 18:43:28 +03:00
2011-05-13 20:51:12 +03:00
if ( spell - > fire )
{
2011-06-25 18:05:01 +03:00
if ( battleTestElementalImmunity ( subject , spell , Bonus : : FIRE_IMMUNITY , damageSpell ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-05-13 20:51:12 +03:00
}
if ( spell - > water )
{
2011-06-25 18:05:01 +03:00
if ( battleTestElementalImmunity ( subject , spell , Bonus : : WATER_IMMUNITY , damageSpell ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-05-13 20:51:12 +03:00
}
if ( spell - > earth )
{
2011-06-25 18:05:01 +03:00
if ( battleTestElementalImmunity ( subject , spell , Bonus : : EARTH_IMMUNITY , damageSpell ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-05-13 20:51:12 +03:00
}
if ( spell - > air )
{
2011-06-25 18:05:01 +03:00
if ( battleTestElementalImmunity ( subject , spell , Bonus : : AIR_IMMUNITY , damageSpell ) )
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-05-13 20:51:12 +03:00
}
2012-01-27 10:25:29 +03:00
if ( vstd : : contains ( VLC - > spellh - > mindSpells , spell - > id ) )
{
if ( subject - > hasBonusOfType ( Bonus : : MIND_IMMUNITY ) )
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
}
2011-05-13 20:51:12 +03:00
2012-02-29 16:47:57 +03:00
if ( risingSpell )
2011-10-08 09:59:36 +03:00
{
if ( subject - > count > = subject - > baseAmount ) //TODO: calculate potential hp raised
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-10-08 09:59:36 +03:00
}
2011-09-06 16:59:26 +03:00
TBonusListPtr immunities = subject - > getBonuses ( Selector : : type ( Bonus : : LEVEL_SPELL_IMMUNITY ) ) ;
2011-02-22 15:44:34 +02:00
if ( subject - > hasBonusOfType ( Bonus : : NEGATE_ALL_NATURAL_IMMUNITIES ) )
{
2011-07-13 21:39:02 +03:00
//std::remove_if(immunities->begin(), immunities->end(), NegateRemover);
immunities - > remove_if ( NegateRemover ) ;
2011-02-22 15:44:34 +02:00
}
2011-04-23 20:10:54 +03:00
if ( subject - > hasBonusOfType ( Bonus : : SPELL_IMMUNITY , spell - > id ) | |
2011-06-25 16:53:15 +03:00
( immunities - > size ( ) > 0 & & immunities - > totalValue ( ) > = spell - > level & & spell - > level ) )
2011-04-23 20:10:54 +03:00
{
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : STACK_IMMUNE_TO_SPELL ;
2011-02-21 18:53:23 +02:00
}
}
2012-02-29 16:47:57 +03:00
else //no target stack on this tile
2011-02-21 18:53:23 +02:00
{
if ( spell - > getTargetType ( ) = = CSpell : : CREATURE | |
2011-12-14 00:23:17 +03:00
( spell - > getTargetType ( ) = = CSpell : : CREATURE_EXPERT_MASSIVE & & mode = = ECastingMode : : HERO_CASTING & & caster & & caster - > getSpellSchoolLevel ( spell ) < 3 ) )
2011-02-20 20:32:39 +02:00
{
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : WRONG_SPELL_TARGET ;
2011-02-20 20:32:39 +02:00
}
}
2011-02-21 18:53:23 +02:00
2011-12-14 00:23:17 +03:00
return ESpellCastProblem : : OK ;
2011-02-20 20:32:39 +02:00
}
2012-01-03 04:55:26 +03:00
std : : vector < ui32 > BattleInfo : : calculateResistedStacks ( const CSpell * sp , const CGHeroInstance * caster , const CGHeroInstance * hero2 , const std : : set < CStack * > affectedCreatures , int casterSideOwner , ECastingMode : : ECastingMode mode , int usedSpellPower , int spellLevel ) const
2011-02-22 15:44:34 +02:00
{
std : : vector < ui32 > ret ;
for ( std : : set < CStack * > : : const_iterator it = affectedCreatures . begin ( ) ; it ! = affectedCreatures . end ( ) ; + + it )
{
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
{
for ( std : : set < CStack * > : : const_iterator it = affectedCreatures . begin ( ) ; it ! = affectedCreatures . end ( ) ; + + it )
{
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 : : getSurrenderingCost ( int player ) const
{
if ( ! battleCanFlee ( player ) ) //to surrender, conditions of fleeing must be fulfilled
return - 1 ;
if ( ! getHero ( theOtherPlayer ( player ) ) ) //additionally, there must be an enemy hero
return - 2 ;
int ret = 0 ;
double discount = 0 ;
BOOST_FOREACH ( const CStack * s , stacks )
if ( s - > owner = = player & & s - > base ) //we pay for our stack that comes from our army (the last condition eliminates summoned cres and war machines)
ret + = s - > getCreature ( ) - > cost [ Res : : GOLD ] * s - > count ;
if ( const CGHeroInstance * h = getHero ( player ) )
discount + = h - > valOfBonuses ( Bonus : : SURRENDER_DISCOUNT ) ;
ret * = ( 100.0 - discount ) / 100.0 ;
2011-12-14 00:23:17 +03:00
vstd : : amax ( ret , 0 ) ; //no negative costs for >100% discounts (impossible in original H3 mechanics, but some day...)
2011-03-05 18:38:22 +02:00
return ret ;
}
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-08 04:43:40 +03:00
const CStack * BattleInfo : : getStackIf ( boost : : function < bool ( const CStack * ) > pred ) const
{
auto stackItr = range : : find_if ( stacks , pred ) ;
return stackItr = = stacks . end ( )
? NULL
: * stackItr ;
}
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-05-18 23:50:16 +03:00
std : : set < BattleHex > BattleInfo : : getStoppers ( bool whichSidePerspective ) const
2012-05-05 00:16:39 +03:00
{
std : : set < BattleHex > ret ;
2012-05-18 23:50:16 +03:00
BOOST_FOREACH ( auto & oi , obstacles )
2012-05-05 00:16:39 +03:00
{
2012-05-18 23:50:16 +03:00
if ( isObstacleVisibleForSide ( * oi , whichSidePerspective ) )
2012-05-05 00:16:39 +03:00
{
2012-05-18 23:50:16 +03:00
range : : copy ( oi - > getStoppingTile ( ) , std : : inserter ( ret , ret . begin ( ) ) ) ;
2012-05-05 00:16:39 +03:00
}
}
return ret ;
}
2012-05-18 23:50:16 +03:00
bool BattleInfo : : hasNativeStack ( ui8 side ) const
{
BOOST_FOREACH ( const CStack * s , stacks )
{
if ( s - > attackerOwned = = ! side & & s - > getCreature ( ) - > isItNativeTerrain ( terrainType ) )
return true ;
}
return false ;
}
bool BattleInfo : : isObstacleVisibleForSide ( const CObstacleInstance & coi , ui8 side ) const
{
return coi . visibleForSide ( side , hasNativeStack ( side ) ) ;
}
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
if ( useBind & & getEffect ( 72 ) )
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 ;
BOOST_FOREACH ( CStack * stack , base - > armyObj - > battle - > getAdjacentCreatures ( 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
2010-12-25 21:23:30 +02:00
{
if ( doubleWide ( ) )
{
if ( attackerOwned )
return position - 1 ;
else
return position + 1 ;
}
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
2011-07-03 08:55:57 +03:00
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > hexes ;
hexes . push_back ( BattleHex ( position ) ) ;
BattleHex occupied = occupiedHex ( ) ;
2011-07-03 22:10:36 +03:00
if ( occupied . isValid ( ) )
hexes . push_back ( occupied ) ;
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 ;
}