2017-07-13 10:26:03 +02:00
/*
* AIUtility . cpp , 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
*
*/
2013-10-18 23:17:25 +03:00
# include "StdInc.h"
# include "AIUtility.h"
# include "VCAI.h"
2018-08-10 18:27:57 +02:00
# include "FuzzyHelper.h"
2013-10-18 23:17:25 +03:00
# include "../../lib/UnlockGuard.h"
# include "../../lib/CConfigHandler.h"
2013-10-20 16:51:05 +03:00
# include "../../lib/CHeroHandler.h"
2014-06-22 13:39:40 +03:00
# include "../../lib/mapObjects/CBank.h"
2015-12-02 21:05:10 +02:00
# include "../../lib/mapObjects/CGTownInstance.h"
# include "../../lib/mapObjects/CQuest.h"
2015-12-02 22:10:46 +02:00
# include "../../lib/mapping/CMapDefines.h"
2013-10-18 23:17:25 +03:00
extern boost : : thread_specific_ptr < CCallback > cb ;
extern boost : : thread_specific_ptr < VCAI > ai ;
2018-04-07 10:44:14 +02:00
extern FuzzyHelper * fh ;
2013-10-18 23:17:25 +03:00
//extern static const int3 dirs[8];
2014-02-15 11:10:06 +03:00
const CGObjectInstance * ObjectIdRef : : operator - > ( ) const
{
return cb - > getObj ( id , false ) ;
}
2018-04-07 10:44:14 +02:00
ObjectIdRef : : operator const CGObjectInstance * ( ) const
2014-02-15 11:10:06 +03:00
{
return cb - > getObj ( id , false ) ;
}
2018-04-07 10:44:14 +02:00
ObjectIdRef : : ObjectIdRef ( ObjectInstanceID _id )
: id ( _id )
2014-02-15 11:10:06 +03:00
{
}
2018-04-07 10:44:14 +02:00
ObjectIdRef : : ObjectIdRef ( const CGObjectInstance * obj )
: id ( obj - > id )
2014-02-15 11:10:06 +03:00
{
}
2018-04-07 10:44:14 +02:00
bool ObjectIdRef : : operator < ( const ObjectIdRef & rhs ) const
2014-02-15 11:10:06 +03:00
{
return id < rhs . id ;
}
2018-04-07 10:44:14 +02:00
HeroPtr : : HeroPtr ( const CGHeroInstance * H )
2014-02-15 11:10:06 +03:00
{
if ( ! H )
{
//init from nullptr should equal to default init
* this = HeroPtr ( ) ;
return ;
}
h = H ;
name = h - > name ;
hid = H - > id ;
// infosCount[ai->playerID][hid]++;
}
HeroPtr : : HeroPtr ( )
{
h = nullptr ;
hid = ObjectInstanceID ( ) ;
}
HeroPtr : : ~ HeroPtr ( )
{
2018-04-07 10:44:14 +02:00
// if(hid >= 0)
// infosCount[ai->playerID][hid]--;
2014-02-15 11:10:06 +03:00
}
2018-04-07 10:44:14 +02:00
bool HeroPtr : : operator < ( const HeroPtr & rhs ) const
2014-02-15 11:10:06 +03:00
{
return hid < rhs . hid ;
}
2017-07-15 13:08:20 +02:00
const CGHeroInstance * HeroPtr : : get ( bool doWeExpectNull ) const
2014-02-15 11:10:06 +03:00
{
//TODO? check if these all assertions every time we get info about hero affect efficiency
//
//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
assert ( doWeExpectNull | | h ) ;
if ( h )
{
auto obj = cb - > getObj ( hid ) ;
const bool owned = obj & & obj - > tempOwner = = ai - > playerID ;
if ( doWeExpectNull & & ! owned )
{
return nullptr ;
}
else
{
assert ( obj ) ;
assert ( owned ) ;
}
}
return h ;
}
const CGHeroInstance * HeroPtr : : operator - > ( ) const
{
return get ( ) ;
}
bool HeroPtr : : validAndSet ( ) const
{
return get ( true ) ;
}
const CGHeroInstance * HeroPtr : : operator * ( ) const
{
return get ( ) ;
}
2018-07-26 12:06:55 +02:00
bool HeroPtr : : operator = = ( const HeroPtr & rhs ) const
{
return h = = rhs . get ( true ) ;
}
2018-04-07 10:44:14 +02:00
void foreach_tile_pos ( std : : function < void ( const int3 & pos ) > foo )
2013-10-18 23:17:25 +03:00
{
2014-03-23 19:36:16 +03:00
// some micro-optimizations since this function gets called a LOT
// callback pointer is thread-specific and slow to retrieve -> read map size only once
int3 mapSize = cb - > getMapSize ( ) ;
for ( int i = 0 ; i < mapSize . x ; i + + )
2018-04-07 10:44:14 +02:00
{
2014-03-23 19:36:16 +03:00
for ( int j = 0 ; j < mapSize . y ; j + + )
2018-04-07 10:44:14 +02:00
{
2014-03-23 19:36:16 +03:00
for ( int k = 0 ; k < mapSize . z ; k + + )
2018-04-07 10:44:14 +02:00
foo ( int3 ( i , j , k ) ) ;
}
}
2013-10-18 23:17:25 +03:00
}
2018-04-07 10:44:14 +02:00
void foreach_tile_pos ( CCallback * cbp , std : : function < void ( CCallback * cbp , const int3 & pos ) > foo )
2014-04-01 14:53:28 +03:00
{
int3 mapSize = cbp - > getMapSize ( ) ;
for ( int i = 0 ; i < mapSize . x ; i + + )
2018-04-07 10:44:14 +02:00
{
2014-04-01 14:53:28 +03:00
for ( int j = 0 ; j < mapSize . y ; j + + )
2018-04-07 10:44:14 +02:00
{
2014-04-01 14:53:28 +03:00
for ( int k = 0 ; k < mapSize . z ; k + + )
2018-04-07 10:44:14 +02:00
foo ( cbp , int3 ( i , j , k ) ) ;
}
}
2014-04-01 14:53:28 +03:00
}
2018-04-07 10:44:14 +02:00
void foreach_neighbour ( const int3 & pos , std : : function < void ( const int3 & pos ) > foo )
2013-10-18 23:17:25 +03:00
{
2014-03-23 19:36:16 +03:00
CCallback * cbp = cb . get ( ) ; // avoid costly retrieval of thread-specific pointer
2018-04-07 10:44:14 +02:00
for ( const int3 & dir : int3 : : getDirs ( ) )
2013-10-18 23:17:25 +03:00
{
const int3 n = pos + dir ;
2014-03-23 19:36:16 +03:00
if ( cbp - > isInTheMap ( n ) )
2018-04-07 10:44:14 +02:00
foo ( pos + dir ) ;
2013-10-18 23:17:25 +03:00
}
}
2018-04-07 10:44:14 +02:00
void foreach_neighbour ( CCallback * cbp , const int3 & pos , std : : function < void ( CCallback * cbp , const int3 & pos ) > foo )
2014-04-01 14:53:28 +03:00
{
2018-04-07 10:44:14 +02:00
for ( const int3 & dir : int3 : : getDirs ( ) )
2014-04-01 14:53:28 +03:00
{
const int3 n = pos + dir ;
if ( cbp - > isInTheMap ( n ) )
2018-04-07 10:44:14 +02:00
foo ( cbp , pos + dir ) ;
2014-04-01 14:53:28 +03:00
}
}
2018-04-07 10:44:14 +02:00
bool CDistanceSorter : : operator ( ) ( const CGObjectInstance * lhs , const CGObjectInstance * rhs )
2013-10-18 23:17:25 +03:00
{
2018-04-07 10:44:14 +02:00
const CGPathNode * ln = ai - > myCb - > getPathsInfo ( hero ) - > getPathInfo ( lhs - > visitablePos ( ) ) ;
const CGPathNode * rn = ai - > myCb - > getPathsInfo ( hero ) - > getPathInfo ( rhs - > visitablePos ( ) ) ;
2014-09-21 16:42:08 +03:00
2013-10-18 23:17:25 +03:00
if ( ln - > turns ! = rn - > turns )
return ln - > turns < rn - > turns ;
return ( ln - > moveRemains > rn - > moveRemains ) ;
}
bool compareMovement ( HeroPtr lhs , HeroPtr rhs )
{
return lhs - > movement > rhs - > movement ;
}
ui64 evaluateDanger ( crint3 tile )
{
2018-04-07 10:44:14 +02:00
const TerrainTile * t = cb - > getTile ( tile , false ) ;
2013-10-18 23:17:25 +03:00
if ( ! t ) //we can know about guard but can't check its tile (the edge of fow)
return 190000000 ; //MUCH
2018-04-07 10:44:14 +02:00
ui64 objectDanger = 0 ;
ui64 guardDanger = 0 ;
2013-10-18 23:17:25 +03:00
auto visObjs = cb - > getVisitableObjs ( tile ) ;
if ( visObjs . size ( ) )
objectDanger = evaluateDanger ( visObjs . back ( ) ) ;
2014-04-01 14:53:28 +03:00
int3 guardPos = cb - > getGuardingCreaturePosition ( tile ) ;
2013-10-18 23:17:25 +03:00
if ( guardPos . x > = 0 & & guardPos ! = tile )
guardDanger = evaluateDanger ( guardPos ) ;
//TODO mozna odwiedzic blockvis nie ruszajac straznika
return std : : max ( objectDanger , guardDanger ) ;
}
2018-04-07 10:44:14 +02:00
ui64 evaluateDanger ( crint3 tile , const CGHeroInstance * visitor )
2013-10-18 23:17:25 +03:00
{
2018-04-07 10:44:14 +02:00
const TerrainTile * t = cb - > getTile ( tile , false ) ;
2013-10-18 23:17:25 +03:00
if ( ! t ) //we can know about guard but can't check its tile (the edge of fow)
return 190000000 ; //MUCH
2018-04-07 10:44:14 +02:00
ui64 objectDanger = 0 ;
ui64 guardDanger = 0 ;
2013-10-18 23:17:25 +03:00
auto visitableObjects = cb - > getVisitableObjs ( tile ) ;
// in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero.
if ( vstd : : contains_if ( visitableObjects , objWithID < Obj : : HERO > ) )
2018-04-07 10:44:14 +02:00
{
2013-10-18 23:17:25 +03:00
vstd : : erase_if ( visitableObjects , [ ] ( const CGObjectInstance * obj )
{
return ! objWithID < Obj : : HERO > ( obj ) ;
} ) ;
2018-04-07 10:44:14 +02:00
}
2013-10-18 23:17:25 +03:00
if ( const CGObjectInstance * dangerousObject = vstd : : backOrNull ( visitableObjects ) )
{
objectDanger = evaluateDanger ( dangerousObject ) ; //unguarded objects can also be dangerous or unhandled
2018-04-07 10:44:14 +02:00
if ( objectDanger )
2013-10-18 23:17:25 +03:00
{
2014-03-23 15:59:03 +03:00
//TODO: don't downcast objects AI shouldn't know about!
2018-04-07 10:44:14 +02:00
auto armedObj = dynamic_cast < const CArmedInstance * > ( dangerousObject ) ;
if ( armedObj )
2014-11-22 19:57:17 +02:00
{
2018-08-05 20:59:04 +02:00
float tacticalAdvantage = fh - > tacticalAdvantageEngine . getTacticalAdvantage ( visitor , armedObj ) ;
2014-11-22 19:57:17 +02:00
objectDanger * = tacticalAdvantage ; //this line tends to go infinite for allied towns (?)
}
2013-10-18 23:17:25 +03:00
}
2018-04-07 10:44:14 +02:00
if ( dangerousObject - > ID = = Obj : : SUBTERRANEAN_GATE )
{
//check guard on the other side of the gate
2014-02-21 10:48:38 +03:00
auto it = ai - > knownSubterraneanGates . find ( dangerousObject ) ;
2018-04-07 10:44:14 +02:00
if ( it ! = ai - > knownSubterraneanGates . end ( ) )
2014-02-21 10:48:38 +03:00
{
auto guards = cb - > getGuardingCreatures ( it - > second - > visitablePos ( ) ) ;
2018-04-07 10:44:14 +02:00
for ( auto cre : guards )
2014-02-21 10:48:38 +03:00
{
2018-08-05 20:59:04 +02:00
vstd : : amax ( guardDanger , evaluateDanger ( cre ) * fh - > tacticalAdvantageEngine . getTacticalAdvantage ( visitor , dynamic_cast < const CArmedInstance * > ( cre ) ) ) ;
2014-02-21 10:48:38 +03:00
}
}
}
2013-10-18 23:17:25 +03:00
}
auto guards = cb - > getGuardingCreatures ( tile ) ;
2018-04-07 10:44:14 +02:00
for ( auto cre : guards )
2013-10-18 23:17:25 +03:00
{
2018-08-05 20:59:04 +02:00
vstd : : amax ( guardDanger , evaluateDanger ( cre ) * fh - > tacticalAdvantageEngine . getTacticalAdvantage ( visitor , dynamic_cast < const CArmedInstance * > ( cre ) ) ) ; //we are interested in strongest monster around
2013-10-18 23:17:25 +03:00
}
//TODO mozna odwiedzic blockvis nie ruszajac straznika
return std : : max ( objectDanger , guardDanger ) ;
}
2018-04-07 10:44:14 +02:00
ui64 evaluateDanger ( const CGObjectInstance * obj )
2013-10-18 23:17:25 +03:00
{
if ( obj - > tempOwner < PlayerColor : : PLAYER_LIMIT & & cb - > getPlayerRelations ( obj - > tempOwner , ai - > playerID ) ! = PlayerRelations : : ENEMIES ) //owned or allied objects don't pose any threat
return 0 ;
switch ( obj - > ID )
{
case Obj : : HERO :
2018-04-07 10:44:14 +02:00
{
InfoAboutHero iah ;
cb - > getHeroInfo ( obj , iah ) ;
return iah . army . getStrength ( ) ;
}
2013-10-18 23:17:25 +03:00
case Obj : : TOWN :
2018-04-07 10:44:14 +02:00
case Obj : : GARRISON :
case Obj : : GARRISON2 :
{
InfoAboutTown iat ;
cb - > getTownInfo ( obj , iat ) ;
return iat . army . getStrength ( ) ;
}
2013-10-18 23:17:25 +03:00
case Obj : : MONSTER :
2018-04-07 10:44:14 +02:00
{
//TODO!!!!!!!!
const CGCreature * cre = dynamic_cast < const CGCreature * > ( obj ) ;
return cre - > getArmyStrength ( ) ;
}
2013-10-18 23:17:25 +03:00
case Obj : : CREATURE_GENERATOR1 :
2016-11-28 03:43:09 +02:00
case Obj : : CREATURE_GENERATOR4 :
2018-04-07 10:44:14 +02:00
{
const CGDwelling * d = dynamic_cast < const CGDwelling * > ( obj ) ;
return d - > getArmyStrength ( ) ;
}
2013-10-18 23:17:25 +03:00
case Obj : : MINE :
case Obj : : ABANDONED_MINE :
2018-04-07 10:44:14 +02:00
{
const CArmedInstance * a = dynamic_cast < const CArmedInstance * > ( obj ) ;
return a - > getArmyStrength ( ) ;
}
2013-10-18 23:17:25 +03:00
case Obj : : CRYPT : //crypt
case Obj : : CREATURE_BANK : //crebank
case Obj : : DRAGON_UTOPIA :
case Obj : : SHIPWRECK : //shipwreck
case Obj : : DERELICT_SHIP : //derelict ship
// case Obj::PYRAMID:
2018-04-07 10:44:14 +02:00
return fh - > estimateBankDanger ( dynamic_cast < const CBank * > ( obj ) ) ;
2013-10-18 23:17:25 +03:00
case Obj : : PYRAMID :
2018-04-07 10:44:14 +02:00
{
if ( obj - > subID = = 0 )
return fh - > estimateBankDanger ( dynamic_cast < const CBank * > ( obj ) ) ;
else
return 0 ;
}
2013-10-18 23:17:25 +03:00
default :
return 0 ;
}
}
2018-04-07 10:44:14 +02:00
bool compareDanger ( const CGObjectInstance * lhs , const CGObjectInstance * rhs )
2013-10-18 23:17:25 +03:00
{
return evaluateDanger ( lhs ) < evaluateDanger ( rhs ) ;
}
bool isSafeToVisit ( HeroPtr h , crint3 tile )
{
2018-04-07 10:44:14 +02:00
const ui64 heroStrength = h - > getTotalStrength ( ) ;
const ui64 dangerStrength = evaluateDanger ( tile , * h ) ;
2013-10-18 23:17:25 +03:00
if ( dangerStrength )
{
if ( heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength )
{
2017-08-11 19:03:05 +02:00
logAi - > trace ( " It's safe for %s to visit tile %s " , h - > name , tile . toString ( ) ) ;
2013-10-18 23:17:25 +03:00
return true ;
}
else
2018-04-07 10:44:14 +02:00
{
2013-10-18 23:17:25 +03:00
return false ;
2018-04-07 10:44:14 +02:00
}
2013-10-18 23:17:25 +03:00
}
return true ; //there's no danger
}
2018-08-08 10:52:52 +02:00
bool isObjectRemovable ( const CGObjectInstance * obj )
{
//FIXME: move logic to object property!
switch ( obj - > ID )
{
case Obj : : MONSTER :
case Obj : : RESOURCE :
case Obj : : CAMPFIRE :
case Obj : : TREASURE_CHEST :
case Obj : : ARTIFACT :
case Obj : : BORDERGUARD :
return true ;
break ;
default :
return false ;
break ;
}
}
2018-04-07 10:44:14 +02:00
bool canBeEmbarkmentPoint ( const TerrainTile * t , bool fromWater )
2013-10-18 23:17:25 +03:00
{
2018-04-07 10:44:14 +02:00
// TODO: Such information should be provided by pathfinder
// Tile must be free or with unoccupied boat
if ( ! t - > blocked )
{
return true ;
}
else if ( ! fromWater ) // do not try to board when in water sector
{
if ( t - > visitableObjects . size ( ) = = 1 & & t - > topVisitableId ( ) = = Obj : : BOAT )
return true ;
}
return false ;
2013-10-18 23:17:25 +03:00
}
int3 whereToExplore ( HeroPtr h )
{
2018-04-07 10:44:14 +02:00
TimeCheck tc ( " where to explore " ) ;
2016-01-31 17:01:58 +02:00
int radius = h - > getSightRadius ( ) ;
2013-10-18 23:17:25 +03:00
int3 hpos = h - > visitablePos ( ) ;
2015-12-04 00:10:51 +02:00
auto sm = ai - > getCachedSectorMap ( h ) ;
2015-03-30 15:32:23 +02:00
2013-10-18 23:17:25 +03:00
//look for nearby objs -> visit them if they're close enouh
const int DIST_LIMIT = 3 ;
std : : vector < const CGObjectInstance * > nearbyVisitableObjs ;
2018-04-07 10:44:14 +02:00
for ( int x = hpos . x - DIST_LIMIT ; x < = hpos . x + DIST_LIMIT ; + + x ) //get only local objects instead of all possible objects on the map
2013-10-18 23:17:25 +03:00
{
2018-04-07 10:44:14 +02:00
for ( int y = hpos . y - DIST_LIMIT ; y < = hpos . y + DIST_LIMIT ; + + y )
2013-12-21 22:17:27 +03:00
{
2018-04-07 10:44:14 +02:00
for ( auto obj : cb - > getVisitableObjs ( int3 ( x , y , hpos . z ) , false ) )
2013-12-21 22:17:27 +03:00
{
int3 op = obj - > visitablePos ( ) ;
CGPath p ;
2015-11-02 12:25:01 +02:00
ai - > myCb - > getPathsInfo ( h . get ( ) ) - > getPath ( p , op ) ;
2018-04-07 10:44:14 +02:00
if ( p . nodes . size ( ) & & p . endPos ( ) = = op & & p . nodes . size ( ) < = DIST_LIMIT )
{
if ( ai - > isGoodForVisit ( obj , h , * sm ) )
2013-12-21 22:17:27 +03:00
nearbyVisitableObjs . push_back ( obj ) ;
2018-04-07 10:44:14 +02:00
}
2013-12-21 22:17:27 +03:00
}
}
2013-10-18 23:17:25 +03:00
}
2018-04-07 10:44:14 +02:00
vstd : : removeDuplicates ( nearbyVisitableObjs ) ; //one object may occupy multiple tiles
2014-09-21 16:42:08 +03:00
boost : : sort ( nearbyVisitableObjs , CDistanceSorter ( h . get ( ) ) ) ;
2013-10-18 23:17:25 +03:00
if ( nearbyVisitableObjs . size ( ) )
return nearbyVisitableObjs . back ( ) - > visitablePos ( ) ;
2013-12-26 12:53:37 +03:00
try //check if nearby tiles allow us to reveal anything - this is quick
2013-10-18 23:17:25 +03:00
{
return ai - > explorationBestNeighbour ( hpos , radius , h ) ;
}
2018-04-07 10:44:14 +02:00
catch ( cannotFulfillGoalException & e )
2013-10-18 23:17:25 +03:00
{
2013-12-26 12:53:37 +03:00
//perform exhaustive search
2014-02-17 20:28:39 +03:00
return ai - > explorationNewPoint ( h ) ;
2013-10-18 23:17:25 +03:00
}
}
2016-11-28 20:29:11 +02:00
bool isBlockedBorderGate ( int3 tileToHit ) //TODO: is that function needed? should be handled by pathfinder
2013-10-18 23:17:25 +03:00
{
2018-04-07 10:44:14 +02:00
if ( cb - > getTile ( tileToHit ) - > topVisitableId ( ) ! = Obj : : BORDER_GATE )
return false ;
auto gate = dynamic_cast < const CGKeys * > ( cb - > getTile ( tileToHit ) - > topVisitableObj ( ) ) ;
2018-08-08 10:52:52 +02:00
return ! gate - > passableFor ( ai - > playerID ) ;
2013-10-18 23:17:25 +03:00
}
2018-04-07 10:44:14 +02:00
bool isBlockVisitObj ( const int3 & pos )
2016-11-28 20:29:11 +02:00
{
2018-04-07 10:44:14 +02:00
if ( auto obj = cb - > getTopObj ( pos ) )
{
if ( obj - > blockVisit ) //we can't stand on that object
2016-11-28 20:29:11 +02:00
return true ;
2018-04-07 10:44:14 +02:00
}
2016-11-28 20:29:11 +02:00
return false ;
}
2013-10-18 23:17:25 +03:00
2018-06-24 12:30:17 +02:00
bool hasReachableNeighbor ( const int3 & pos , HeroPtr hero , CCallback * cbp )
2018-06-16 13:56:49 +02:00
{
2018-06-24 12:30:17 +02:00
for ( crint3 dir : int3 : : getDirs ( ) )
2018-06-16 13:56:49 +02:00
{
int3 tile = pos + dir ;
2018-06-24 12:30:17 +02:00
if ( cbp - > isInTheMap ( tile ) & & cbp - > getPathsInfo ( hero . get ( ) ) - > getPathInfo ( tile ) - > reachable ( ) )
2018-06-16 13:56:49 +02:00
{
return true ;
}
}
return false ;
}
2018-06-24 12:30:17 +02:00
int howManyTilesWillBeDiscovered ( const int3 & pos , int radious , CCallback * cbp , HeroPtr hero )
2018-04-07 10:44:14 +02:00
{
2013-10-18 23:17:25 +03:00
int ret = 0 ;
2018-06-24 12:30:17 +02:00
for ( int x = pos . x - radious ; x < = pos . x + radious ; x + + )
2013-10-18 23:17:25 +03:00
{
2018-06-24 12:30:17 +02:00
for ( int y = pos . y - radious ; y < = pos . y + radious ; y + + )
2013-10-18 23:17:25 +03:00
{
2018-04-07 10:44:14 +02:00
int3 npos = int3 ( x , y , pos . z ) ;
2018-06-24 12:30:17 +02:00
if ( cbp - > isInTheMap ( npos ) & & pos . dist2d ( npos ) - 0.5 < radious & & ! cbp - > isVisible ( npos ) )
2013-10-18 23:17:25 +03:00
{
2018-06-24 12:30:17 +02:00
if ( hasReachableNeighbor ( npos , hero , cbp ) )
2013-10-18 23:17:25 +03:00
ret + + ;
}
}
}
return ret ;
}
2018-04-07 10:44:14 +02:00
void getVisibleNeighbours ( const std : : vector < int3 > & tiles , std : : vector < int3 > & out )
2013-10-18 23:17:25 +03:00
{
2018-04-07 10:44:14 +02:00
for ( const int3 & tile : tiles )
2013-10-18 23:17:25 +03:00
{
foreach_neighbour ( tile , [ & ] ( int3 neighbour )
{
if ( cb - > isVisible ( neighbour ) )
out . push_back ( neighbour ) ;
} ) ;
}
}
2018-07-26 12:06:55 +02:00
creInfo infoFromDC ( const dwellingContent & dc )
{
creInfo ci ;
ci . count = dc . first ;
ci . creID = dc . second . size ( ) ? dc . second . back ( ) : CreatureID ( - 1 ) ; //should never be accessed
if ( ci . creID ! = - 1 )
{
ci . cre = VLC - > creh - > creatures [ ci . creID ] ;
ci . level = ci . cre - > level ; //this is cretaure tier, while tryRealize expects dwelling level. Ignore.
}
else
{
ci . cre = nullptr ;
ci . level = 0 ;
}
return ci ;
}
ui64 howManyReinforcementsCanBuy ( HeroPtr h , const CGTownInstance * t )
{
ui64 aivalue = 0 ;
int freeHeroSlots = GameConstants : : ARMY_SIZE - h - > stacksCount ( ) ;
2018-07-27 09:22:07 +02:00
for ( auto const dc : t - > creatures )
2018-07-26 12:06:55 +02:00
{
creInfo ci = infoFromDC ( dc ) ;
2018-07-27 09:22:07 +02:00
if ( ci . count & & ci . creID ! = - 1 ) //valid creature at this level
2018-07-26 12:06:55 +02:00
{
//can be merged with another stack?
SlotID dst = h - > getSlotFor ( ci . creID ) ;
if ( ! h - > hasStackAtSlot ( dst ) ) //need another new slot for this stack
if ( ! freeHeroSlots ) //no more place for stacks
continue ;
else
freeHeroSlots - - ; //new slot will be occupied
//we found matching occupied or free slot
aivalue + = ci . count * ci . cre - > AIValue ;
}
}
return aivalue ;
}
2018-04-07 10:44:14 +02:00
ui64 howManyReinforcementsCanGet ( HeroPtr h , const CGTownInstance * t )
2013-10-18 23:17:25 +03:00
{
ui64 ret = 0 ;
int freeHeroSlots = GameConstants : : ARMY_SIZE - h - > stacksCount ( ) ;
std : : vector < const CStackInstance * > toMove ;
for ( auto const slot : t - > Slots ( ) )
{
//can be merged woth another stack?
SlotID dst = h - > getSlotFor ( slot . second - > getCreatureID ( ) ) ;
if ( h - > hasStackAtSlot ( dst ) )
ret + = t - > getPower ( slot . first ) ;
else
toMove . push_back ( slot . second ) ;
}
2018-04-07 10:44:14 +02:00
boost : : sort ( toMove , [ ] ( const CStackInstance * lhs , const CStackInstance * rhs )
2013-10-18 23:17:25 +03:00
{
return lhs - > getPower ( ) < rhs - > getPower ( ) ;
} ) ;
2018-04-07 10:44:14 +02:00
for ( auto & stack : boost : : adaptors : : reverse ( toMove ) )
2013-10-18 23:17:25 +03:00
{
if ( freeHeroSlots )
{
ret + = stack - > getPower ( ) ;
freeHeroSlots - - ;
}
else
break ;
}
return ret ;
}
bool compareHeroStrength ( HeroPtr h1 , HeroPtr h2 )
{
return h1 - > getTotalStrength ( ) < h2 - > getTotalStrength ( ) ;
}
2018-04-07 10:44:14 +02:00
bool compareArmyStrength ( const CArmedInstance * a1 , const CArmedInstance * a2 )
2013-10-18 23:17:25 +03:00
{
return a1 - > getArmyStrength ( ) < a2 - > getArmyStrength ( ) ;
2013-10-20 16:51:05 +03:00
}
2015-04-07 22:48:35 +02:00
2018-04-07 10:44:14 +02:00
bool compareArtifacts ( const CArtifactInstance * a1 , const CArtifactInstance * a2 )
2015-04-07 22:48:35 +02:00
{
auto art1 = a1 - > artType ;
auto art2 = a2 - > artType ;
2016-09-08 03:26:01 +02:00
if ( art1 - > price = = art2 - > price )
return art1 - > valOfBonuses ( Bonus : : PRIMARY_SKILL ) > art2 - > valOfBonuses ( Bonus : : PRIMARY_SKILL ) ;
else if ( art1 - > price > art2 - > price )
2015-04-07 22:48:35 +02:00
return true ;
else
2016-09-08 03:26:01 +02:00
return false ;
2015-10-12 16:08:18 +02:00
}