2012-02-14 21:04:45 +03:00
# include "StdInc.h"
# include "VCAI.h"
2012-02-20 00:03:43 +03:00
# include "../../lib/UnlockGuard.h"
2012-03-03 13:08:01 +03:00
# include "Fuzzy.h"
# include "../../lib/CObjectHandler.h"
2012-02-14 21:04:45 +03:00
# define I_AM_ELEMENTAR return CGoal(*this).setisElementar(true)
2012-02-22 22:22:11 +03:00
CLogger & aiLogger = tlog6 ;
2012-03-10 22:14:45 +03:00
extern FuzzyHelper * fh ;
2012-03-03 13:08:01 +03:00
2012-03-05 22:11:28 +03:00
class CGVisitableOPW ;
2012-02-14 21:04:45 +03:00
const int ACTUAL_RESOURCE_COUNT = 7 ;
2012-03-04 13:43:46 +03:00
const double SAFE_ATTACK_CONSTANT = 1.5 ;
2012-02-14 21:04:45 +03:00
using namespace vstd ;
//one thread may be turn of AI and another will be handling a side effect for AI2
boost : : thread_specific_ptr < CCallback > cb ;
boost : : thread_specific_ptr < VCAI > ai ;
2012-03-03 13:08:01 +03:00
2012-02-14 21:04:45 +03:00
// CCallback *cb;
// VCAI *ai;
//helper RAII to manage global ai/cb ptrs
struct SetGlobalState
{
SetGlobalState ( VCAI * AI )
{
assert ( ! ai . get ( ) ) ;
assert ( ! cb . get ( ) ) ;
ai . reset ( AI ) ;
cb . reset ( AI - > myCb ) ;
}
~ SetGlobalState ( )
{
ai . release ( ) ;
cb . release ( ) ;
}
} ;
# define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai);
# define NET_EVENT_HANDLER SET_GLOBAL_STATE(this)
# define MAKING_TURN SET_GLOBAL_STATE(this)
const int GOLD_RESERVE = 10000 ; //when buying creatures we want to keep at least this much gold (10000 so at least we'll be able to reach capitol)
const int HERO_GOLD_COST = 2500 ;
const int ALLOWED_ROAMING_HEROES = 8 ;
const int GOLD_MINE_PRODUCTION = 1000 , WOOD_ORE_MINE_PRODUCTION = 2 , RESOURCE_MINE_PRODUCTION = 1 ;
2012-03-13 23:33:00 +03:00
std : : string goalName ( EGoals goalType )
{
switch ( goalType )
{
case INVALID :
return " INVALID " ;
case WIN :
return " WIN " ;
case CONQUER :
return " CONQUER " ;
case EXPLORE :
return " EXPLORE " ;
case GATHER_ARMY :
return " GATHER ARMY " ;
case VISIT_TILE :
return " VISIT TILE " ;
case CLEAR_WAY_TO :
return " CLEAR WAY TO " ;
default :
return boost : : lexical_cast < std : : string > ( goalType ) ;
}
}
2012-02-14 21:04:45 +03:00
bool compareHeroStrength ( const CGHeroInstance * h1 , const CGHeroInstance * h2 )
{
return h1 - > getTotalStrength ( ) < h2 - > getTotalStrength ( ) ;
}
bool compareArmyStrength ( const CArmedInstance * a1 , const CArmedInstance * a2 )
{
return a1 - > getArmyStrength ( ) < a2 - > getArmyStrength ( ) ;
}
static const int3 dirs [ ] = { int3 ( 0 , 1 , 0 ) , int3 ( 0 , - 1 , 0 ) , int3 ( - 1 , 0 , 0 ) , int3 ( + 1 , 0 , 0 ) ,
int3 ( 1 , 1 , 0 ) , int3 ( - 1 , 1 , 0 ) , int3 ( 1 , - 1 , 0 ) , int3 ( - 1 , - 1 , 0 ) } ;
struct AILogger
{
AILogger ( )
{
lvl = 0 ;
}
int lvl ;
struct Tab
{
2012-02-16 20:10:58 +03:00
Tab ( ) ;
~ Tab ( ) ;
2012-02-14 21:04:45 +03:00
} ;
} logger ;
2012-02-16 20:10:58 +03:00
AILogger : : Tab : : Tab ( )
{
logger . lvl + + ;
}
AILogger : : Tab : : ~ Tab ( )
{
logger . lvl - - ;
}
2012-02-14 21:04:45 +03:00
struct TimeCheck
{
CStopWatch time ;
std : : string txt ;
TimeCheck ( crstring TXT ) : txt ( TXT )
{
}
~ TimeCheck ( )
{
BNLOG ( " Time of %s was %d ms. " , txt % time . getDiff ( ) ) ;
}
} ;
template < typename T >
void removeDuplicates ( std : : vector < T > & vec )
{
boost : : sort ( vec ) ;
vec . erase ( std : : unique ( vec . begin ( ) , vec . end ( ) ) , vec . end ( ) ) ;
}
template < typename Range , typename Predicate >
void erase_if ( Range & vec , Predicate pred )
{
vec . erase ( boost : : remove_if ( vec , pred ) , vec . end ( ) ) ;
}
struct AtScopeExit
{
boost : : function < void ( ) > foo ;
2012-02-16 20:10:58 +03:00
AtScopeExit ( const boost : : function < void ( ) > & FOO ) : foo ( FOO )
2012-02-14 21:04:45 +03:00
{ }
~ AtScopeExit ( )
{
foo ( ) ;
}
} ;
void foreach_tile_pos ( boost : : function < void ( const int3 & pos ) > foo )
{
for ( int i = 0 ; i < cb - > getMapSize ( ) . x ; i + + )
for ( int j = 0 ; j < cb - > getMapSize ( ) . y ; j + + )
for ( int k = 0 ; k < cb - > getMapSize ( ) . z ; k + + )
foo ( int3 ( i , j , k ) ) ;
}
void foreach_neighbour ( const int3 & pos , boost : : function < void ( const int3 & pos ) > foo )
{
BOOST_FOREACH ( const int3 & dir , dirs )
{
const int3 n = pos + dir ;
if ( cb - > isInTheMap ( n ) )
foo ( pos + dir ) ;
}
}
unsigned char & retreiveTileN ( std : : vector < std : : vector < std : : vector < unsigned char > > > & vectors , const int3 & pos )
{
return vectors [ pos . x ] [ pos . y ] [ pos . z ] ;
}
const unsigned char & retreiveTileN ( const std : : vector < std : : vector < std : : vector < unsigned char > > > & vectors , const int3 & pos )
{
return vectors [ pos . x ] [ pos . y ] [ pos . z ] ;
}
void foreach_tile ( std : : vector < std : : vector < std : : vector < unsigned char > > > & vectors , boost : : function < void ( unsigned char & in ) > foo )
{
for ( auto i = vectors . begin ( ) ; i ! = vectors . end ( ) ; i + + )
for ( auto j = i - > begin ( ) ; j ! = i - > end ( ) ; j + + )
for ( auto z = j - > begin ( ) ; z ! = j - > end ( ) ; z + + )
foo ( * z ) ;
}
struct ObjInfo
{
int3 pos ;
std : : string name ;
ObjInfo ( ) { }
ObjInfo ( const CGObjectInstance * obj )
{
pos = obj - > pos ;
name = obj - > getHoverText ( ) ;
}
} ;
std : : map < const CGObjectInstance * , ObjInfo > helperObjInfo ;
template < typename Container , typename Item >
bool remove_if_present ( Container & c , const Item & item )
{
2012-02-16 20:10:58 +03:00
auto i = std : : find ( c . begin ( ) , c . end ( ) , item ) ;
2012-03-13 15:47:47 +03:00
if ( i ! = c . end ( ) )
2012-02-14 21:04:45 +03:00
{
c . erase ( i ) ;
return true ;
}
return false ;
}
2012-03-13 15:47:47 +03:00
template < typename V , typename Item , typename Item2 >
bool remove_if_present ( std : : map < Item , V > & c , const Item2 & item )
{
auto i = c . find ( item ) ;
if ( i ! = c . end ( ) )
{
c . erase ( i ) ;
return true ;
}
return false ;
}
2012-02-14 21:04:45 +03:00
template < typename Container , typename Pred >
2012-02-16 20:10:58 +03:00
void erase ( Container & c , Pred pred )
2012-02-14 21:04:45 +03:00
{
c . erase ( boost : : remove_if ( c , pred ) , c . end ( ) ) ;
}
bool isReachable ( const CGObjectInstance * obj )
{
return cb - > getPathInfo ( obj - > visitablePos ( ) ) - > turns < 255 ;
}
ui64 howManyReinforcementsCanGet ( const CGHeroInstance * h , const CGTownInstance * t )
{
ui64 ret = 0 ;
int freeHeroSlots = GameConstants : : ARMY_SIZE - h - > stacksCount ( ) ;
std : : vector < const CStackInstance * > toMove ;
BOOST_FOREACH ( auto const slot , t - > Slots ( ) )
{
//can be merged woth another stack?
TSlot dst = h - > getSlotFor ( slot . second - > getCreatureID ( ) ) ;
if ( h - > hasStackAtSlot ( dst ) )
ret + = t - > getPower ( slot . first ) ;
else
toMove . push_back ( slot . second ) ;
}
boost : : sort ( toMove , [ ] ( const CStackInstance * lhs , const CStackInstance * rhs )
{
return lhs - > getPower ( ) < rhs - > getPower ( ) ;
} ) ;
BOOST_REVERSE_FOREACH ( const CStackInstance * stack , toMove )
{
if ( freeHeroSlots )
{
ret + = stack - > getPower ( ) ;
freeHeroSlots - - ;
}
else
break ;
}
return ret ;
}
std : : string strFromInt3 ( int3 pos )
{
std : : ostringstream oss ;
oss < < pos ;
return oss . str ( ) ;
}
bool isCloser ( const CGObjectInstance * lhs , const CGObjectInstance * rhs )
{
const CGPathNode * ln = cb - > getPathInfo ( lhs - > visitablePos ( ) ) , * rn = cb - > getPathInfo ( rhs - > visitablePos ( ) ) ;
if ( ln - > turns ! = rn - > turns )
return ln - > turns < rn - > turns ;
return ( ln - > moveRemains > rn - > moveRemains ) ;
} ;
ui64 evaluateDanger ( const CGObjectInstance * obj ) ;
ui64 evaluateDanger ( crint3 tile )
{
const TerrainTile * t = cb - > getTile ( tile , false ) ;
if ( ! t ) //we can know about guard but can't check its tile (the edge of fow)
return 190000000 ; //MUCH
ui64 objectDanger = 0 , guardDanger = 0 ;
if ( t - > visitable )
2012-02-26 21:31:59 +03:00
objectDanger = evaluateDanger ( t - > visitableObjects . back ( ) ) ;
2012-02-16 20:10:58 +03:00
2012-02-14 21:04:45 +03:00
int3 guardPos = cb - > guardingCreaturePosition ( tile ) ;
if ( guardPos . x > = 0 & & guardPos ! = tile )
guardDanger = evaluateDanger ( guardPos ) ;
//TODO mozna odwiedzic blockvis nie ruszajac straznika
return std : : max ( objectDanger , guardDanger ) ;
return 0 ;
}
2012-03-04 13:43:46 +03:00
ui64 evaluateDanger ( crint3 tile , const CGHeroInstance * visitor )
{
const TerrainTile * t = cb - > getTile ( tile , false ) ;
if ( ! t ) //we can know about guard but can't check its tile (the edge of fow)
return 190000000 ; //MUCH
ui64 objectDanger = 0 , guardDanger = 0 ;
CArmedInstance * dangerousObject ;
if ( t - > visitable )
{
dangerousObject = dynamic_cast < CArmedInstance * > ( t - > visitableObjects . back ( ) ) ;
2012-03-13 15:47:47 +03:00
objectDanger = evaluateDanger ( t - > visitableObjects . back ( ) ) ; //unguarded objects can also be dangerous or unhandled
2012-03-04 13:43:46 +03:00
if ( dangerousObject )
{
2012-03-10 22:14:45 +03:00
objectDanger * = fh - > getTacticalAdvantage ( visitor , dangerousObject ) ;
2012-03-04 13:43:46 +03:00
}
}
int3 guardPos = cb - > guardingCreaturePosition ( tile ) ;
if ( guardPos . x > = 0 & & guardPos ! = tile )
guardDanger = evaluateDanger ( guardPos , visitor ) ;
//TODO mozna odwiedzic blockvis nie ruszajac straznika
return std : : max ( objectDanger , guardDanger ) ;
return 0 ;
}
2012-02-14 21:04:45 +03:00
ui64 evaluateDanger ( const CGObjectInstance * obj )
{
if ( obj - > tempOwner < GameConstants : : PLAYER_LIMIT & & cb - > getPlayerRelations ( obj - > tempOwner , ai - > playerID ) ) //owned or allied objects don't pose any threat
return 0 ;
switch ( obj - > ID )
{
case GameConstants : : HEROI_TYPE :
{
InfoAboutHero iah ;
cb - > getHeroInfo ( obj , iah ) ;
return iah . army . getStrength ( ) ;
}
case GameConstants : : TOWNI_TYPE :
case Obj : : GARRISON : case Obj : : GARRISON2 : //garrison
{
InfoAboutTown iat ;
cb - > getTownInfo ( obj , iat ) ;
return iat . army . getStrength ( ) ;
}
case GameConstants : : CREI_TYPE :
{
//TODO!!!!!!!!
const CGCreature * cre = dynamic_cast < const CGCreature * > ( obj ) ;
return cre - > getArmyStrength ( ) ;
}
2012-03-13 15:47:47 +03:00
case Obj : : CREATURE_GENERATOR1 :
{
const CGDwelling * d = dynamic_cast < const CGDwelling * > ( obj ) ;
return d - > getArmyStrength ( ) ;
}
2012-02-14 21:04:45 +03:00
case Obj : : CRYPT : //crypt
case Obj : : CREATURE_BANK : //crebank
2012-03-03 14:39:54 +03:00
case Obj : : DRAGON_UTOPIA :
2012-02-14 21:04:45 +03:00
case Obj : : SHIPWRECK : //shipwreck
case Obj : : DERELICT_SHIP : //derelict ship
2012-03-03 13:08:01 +03:00
case Obj : : PYRAMID :
2012-03-10 22:14:45 +03:00
return fh - > estimateBankDanger ( VLC - > objh - > bankObjToIndex ( obj ) ) ;
2012-02-14 21:04:45 +03:00
default :
return 0 ;
}
}
bool compareDanger ( const CGObjectInstance * lhs , const CGObjectInstance * rhs )
{
return evaluateDanger ( lhs ) < evaluateDanger ( rhs ) ;
}
VCAI : : VCAI ( void )
2012-03-03 13:08:01 +03:00
{
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
myCb = NULL ;
battleAIName = " StupidAI " ;
makingTurn = NULL ;
}
VCAI : : ~ VCAI ( void )
2012-03-03 13:08:01 +03:00
{
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : availableCreaturesChanged ( const CGDwelling * town )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroMoved ( const TryMoveHero & details )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
if ( details . result = = TryMoveHero : : TELEPORTATION )
{
2012-02-18 17:59:37 +03:00
const TerrainTile * t1 = cb - > getTile ( CGHeroInstance : : convertPosition ( details . start , false ) , false ) ,
* t2 = cb - > getTile ( CGHeroInstance : : convertPosition ( details . end , false ) , false ) ;
if ( ! t1 | | ! t2 ) //enemy may have teleported to a tile we don't see
return ;
2012-02-14 21:04:45 +03:00
if ( t1 - > visitable & & t2 - > visitable )
{
2012-02-16 20:10:58 +03:00
const CGObjectInstance * o1 = t1 - > visitableObjects . front ( ) ,
2012-02-14 21:04:45 +03:00
* o2 = t2 - > visitableObjects . front ( ) ;
if ( o1 - > ID = = Obj : : SUBTERRANEAN_GATE & & o2 - > ID = = Obj : : SUBTERRANEAN_GATE )
{
knownSubterraneanGates [ o1 ] = o2 ;
knownSubterraneanGates [ o2 ] = o1 ;
}
}
2012-02-16 20:10:58 +03:00
2012-02-14 21:04:45 +03:00
}
}
void VCAI : : stackChagedCount ( const StackLocation & location , const TQuantity & change , bool isAbsolute )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroInGarrisonChange ( const CGTownInstance * town )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : centerView ( int3 pos , int focusTime )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : artifactMoved ( const ArtifactLocation & src , const ArtifactLocation & dst )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : artifactAssembled ( const ArtifactLocation & al )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : showTavernWindow ( const CGObjectInstance * townOrTavern )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
2012-03-07 17:05:54 +03:00
void VCAI : : showThievesGuildWindow ( const CGObjectInstance * obj )
{
NET_EVENT_HANDLER ;
LOG_ENTRY ;
}
2012-02-14 21:04:45 +03:00
void VCAI : : playerBlocked ( int reason )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
if ( reason = = PlayerBlocked : : UPCOMING_BATTLE )
status . setBattle ( UPCOMING_BATTLE ) ;
}
void VCAI : : showPuzzleMap ( )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : showShipyardDialog ( const IShipyard * obj )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : gameOver ( ui8 player , bool victory )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
BNLOG ( " Player %d: I heard that player %d %s. " , playerID % ( int ) player % ( victory ? " won " : " lost " ) ) ;
if ( player = = playerID )
{
if ( victory )
{
tlog0 < < " VCAI: I won! Incredible! \n " ;
tlog0 < < " Turn nr " < < myCb - > getDate ( ) < < std : : endl ;
}
else
{
tlog0 < < " VCAI: Player " < < ( int ) player < < " lost. It's me. What a disappointment! :( \n " ;
}
2012-02-29 04:31:48 +03:00
// //let's make Impossible difficulty finally standing to its name :>
// if(myCb->getStartInfo()->difficulty == 4 && !victory)
// {
// //play dirty: crash the whole engine to avoid lose
// //that way AI is unbeatable!
// *(int*)NULL = 666;
// }
// TODO - at least write some insults on stdout
2012-02-14 21:04:45 +03:00
2012-02-22 16:41:27 +03:00
finish ( ) ;
2012-02-14 21:04:45 +03:00
}
}
void VCAI : : artifactPut ( const ArtifactLocation & al )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : artifactRemoved ( const ArtifactLocation & al )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : stacksErased ( const StackLocation & location )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : artifactDisassembled ( const ArtifactLocation & al )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroVisit ( const CGHeroInstance * visitor , const CGObjectInstance * visitedObj , bool start )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
2012-03-05 22:11:28 +03:00
if ( start )
{
2012-03-14 16:02:38 +03:00
visitedObject = const_cast < CGObjectInstance * > ( visitedObj ) ; // remember the object and wait for return
markObjectVisited ( visitedObj ) ;
2012-03-29 21:26:06 +03:00
remove_if_present ( reservedObjs , visitedObj ) ; //unreserve objects
remove_if_present ( reservedHeroesMap [ visitor ] , visitedObj ) ;
2012-03-05 22:11:28 +03:00
}
2012-02-14 21:04:45 +03:00
}
void VCAI : : availableArtifactsChanged ( const CGBlackMarket * bm /*= NULL*/ )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroVisitsTown ( const CGHeroInstance * hero , const CGTownInstance * town )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
//buildArmyIn(town);
//moveCreaturesToHero(town);
}
void VCAI : : tileHidden ( const boost : : unordered_set < int3 , ShashInt3 > & pos )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
// BOOST_FOREACH(int3 tile, pos)
// BOOST_FOREACH(const CGObjectInstance *obj, cb->getVisitableObjs(tile))
// remove_if_present(visitableObjs, obj);
visitableObjs . erase ( boost : : remove_if ( visitableObjs , [ & ] ( const CGObjectInstance * obj ) { return ! myCb - > getObj ( obj - > id ) ; } ) , visitableObjs . end ( ) ) ;
}
void VCAI : : tileRevealed ( const boost : : unordered_set < int3 , ShashInt3 > & pos )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
BOOST_FOREACH ( int3 tile , pos )
BOOST_FOREACH ( const CGObjectInstance * obj , myCb - > getVisitableObjs ( tile ) )
addVisitableObj ( obj ) ;
}
void VCAI : : heroExchangeStarted ( si32 hero1 , si32 hero2 )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroPrimarySkillChanged ( const CGHeroInstance * hero , int which , si64 val )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : showRecruitmentDialog ( const CGDwelling * dwelling , const CArmedInstance * dst , int level )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroMovePointsChanged ( const CGHeroInstance * hero )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : stackChangedType ( const StackLocation & location , const CCreature & newType )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : stacksRebalanced ( const StackLocation & src , const StackLocation & dst , TQuantity count )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : newObject ( const CGObjectInstance * obj )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
if ( obj - > isVisitable ( ) )
addVisitableObj ( obj ) ;
}
void VCAI : : objectRemoved ( const CGObjectInstance * obj )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
if ( remove_if_present ( visitableObjs , obj ) )
assert ( obj - > isVisitable ( ) ) ;
}
void VCAI : : showHillFortWindow ( const CGObjectInstance * object , const CGHeroInstance * visitor )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : playerBonusChanged ( const Bonus & bonus , bool gain )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : newStackInserted ( const StackLocation & location , const CStackInstance & stack )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroCreated ( const CGHeroInstance * )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : advmapSpellCast ( const CGHeroInstance * caster , int spellID )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : showInfoDialog ( const std : : string & text , const std : : vector < Component * > & components , int soundID )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : requestRealized ( PackageApplied * pa )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
if ( status . haveTurn ( ) )
{
if ( pa - > packType = = typeList . getTypeID < EndTurn > ( ) )
if ( pa - > result )
status . madeTurn ( ) ;
}
if ( pa - > packType = = typeList . getTypeID < QueryReply > ( ) )
{
status . removeQuery ( ) ;
}
}
void VCAI : : receivedResource ( int type , int val )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : stacksSwapped ( const StackLocation & loc1 , const StackLocation & loc2 )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : showUniversityWindow ( const IMarket * market , const CGHeroInstance * visitor )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroManaPointsChanged ( const CGHeroInstance * hero )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroSecondarySkillChanged ( const CGHeroInstance * hero , int which , int val )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : battleResultsApplied ( )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
assert ( status . getBattle ( ) = = ENDING_BATTLE ) ;
status . setBattle ( NO_BATTLE ) ;
}
void VCAI : : objectPropertyChanged ( const SetObjectProperty * sop )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
if ( sop - > what = = ObjProperty : : OWNER )
{
if ( sop - > val = = playerID )
remove_if_present ( visitableObjs , myCb - > getObj ( sop - > id ) ) ;
//TODO restore lost obj
}
}
void VCAI : : buildChanged ( const CGTownInstance * town , int buildingID , int what )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : heroBonusChanged ( const CGHeroInstance * hero , const Bonus & bonus , bool gain )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : showMarketWindow ( const IMarket * market , const CGHeroInstance * visitor )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : init ( CCallback * CB )
{
myCb = CB ;
cbc = CB ;
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
playerID = myCb - > getMyColor ( ) ;
myCb - > waitTillRealize = true ;
2012-02-17 00:46:28 +03:00
myCb - > unlockGsWhenWaiting = true ;
2012-02-16 20:10:58 +03:00
2012-03-10 22:14:45 +03:00
if ( ! fh )
fh = new FuzzyHelper ( ) ;
2012-02-14 21:04:45 +03:00
retreiveVisitableObjs ( visitableObjs ) ;
}
void VCAI : : yourTurn ( )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
status . startedTurn ( ) ;
makingTurn = new boost : : thread ( & VCAI : : makeTurn , this ) ;
}
void VCAI : : heroGotLevel ( const CGHeroInstance * hero , int pskill , std : : vector < ui16 > & skills , boost : : function < void ( ui32 ) > & callback )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
status . addQuery ( ) ;
2012-03-26 01:46:14 +03:00
requestActionASAP ( boost : : bind ( callback , 0 ) ) ;
2012-02-14 21:04:45 +03:00
}
void VCAI : : showBlockingDialog ( const std : : string & text , const std : : vector < Component > & components , ui32 askID , const int soundID , bool selection , bool cancel )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
int sel = 0 ;
status . addQuery ( ) ;
if ( selection ) //select from multiple components -> take the last one (they're indexed [1-size])
sel = components . size ( ) ;
if ( ! selection & & cancel ) //yes&no -> always answer yes, we are a brave AI :)
sel = 1 ;
2012-03-26 14:38:51 +03:00
requestActionASAP ( [ = ] ( )
2012-03-26 01:46:14 +03:00
{
cb - > selectionMade ( sel , askID ) ;
} ) ;
2012-02-14 21:04:45 +03:00
}
void VCAI : : showGarrisonDialog ( const CArmedInstance * up , const CGHeroInstance * down , bool removableUnits , boost : : function < void ( ) > & onEnd )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
status . addQuery ( ) ;
2012-03-11 19:29:01 +03:00
//you can't request action from action-response thread
2012-04-08 13:34:23 +03:00
requestActionASAP ( [ = ] ( )
2012-03-26 01:46:14 +03:00
{
pickBestCreatures ( down , up ) ;
onEnd ( ) ;
} ) ;
2012-02-14 21:04:45 +03:00
}
void VCAI : : serialize ( COSer < CSaveFile > & h , const int version )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void VCAI : : serialize ( CISer < CLoadFile > & h , const int version )
{
2012-03-03 13:08:01 +03:00
NET_EVENT_HANDLER ;
2012-02-14 21:04:45 +03:00
LOG_ENTRY ;
}
void makePossibleUpgrades ( const CArmedInstance * obj )
{
if ( ! obj )
return ;
for ( int i = 0 ; i < GameConstants : : ARMY_SIZE ; i + + )
{
if ( const CStackInstance * s = obj - > getStackPtr ( i ) )
{
UpgradeInfo ui ;
cb - > getUpgradeInfo ( obj , i , ui ) ;
if ( ui . oldID > = 0 & & cb - > getResourceAmount ( ) . canAfford ( ui . cost [ 0 ] * s - > count ) )
{
cb - > upgradeCreature ( obj , i , ui . newID [ 0 ] ) ;
}
}
}
}
void VCAI : : makeTurn ( )
{
MAKING_TURN ;
boost : : shared_lock < boost : : shared_mutex > gsLock ( cb - > getGsMutex ( ) ) ;
setThreadName ( - 1 , " VCAI::makeTurn " ) ;
BNLOG ( " Player %d starting turn " , playerID ) ;
INDENT ;
2012-03-13 15:47:47 +03:00
switch ( cb - > getDate ( 1 ) )
2012-02-14 21:04:45 +03:00
{
2012-03-13 15:47:47 +03:00
case 1 :
2012-02-14 21:04:45 +03:00
{
2012-03-13 15:47:47 +03:00
townVisitsThisWeek . clear ( ) ;
std : : vector < const CGObjectInstance * > objs ;
retreiveVisitableObjs ( objs , true ) ;
BOOST_FOREACH ( const CGObjectInstance * obj , objs )
{
if ( isWeeklyRevisitable ( obj ) )
{
if ( ! vstd : : contains ( visitableObjs , obj ) )
visitableObjs . push_back ( obj ) ;
auto o = std : : find ( alreadyVisited . begin ( ) , alreadyVisited . end ( ) , obj ) ;
if ( o ! = alreadyVisited . end ( ) )
alreadyVisited . erase ( o ) ;
}
}
}
2012-03-13 23:33:00 +03:00
break ;
2012-03-13 15:47:47 +03:00
case 7 : //reconsider strategy
{
const CGHeroInstance * h = primaryHero ( ) ;
if ( h ) //check if our primary hero can ahndle danger
{
ui64 totalDanger = 0 ;
int dangerousObjects = 0 ;
std : : vector < const CGObjectInstance * > objs ;
retreiveVisitableObjs ( objs , false ) ;
BOOST_FOREACH ( auto obj , objs )
{
if ( evaluateDanger ( obj ) ) //potentilaly dnagerous
{
totalDanger + = evaluateDanger ( obj - > visitablePos ( ) , h ) ;
+ + dangerousObjects ;
}
}
if ( dangerousObjects & & totalDanger / dangerousObjects > h - > getHeroStrength ( ) )
{
setGoal ( h , CGoal ( GATHER_ARMY ) . sethero ( h ) ) ;
}
2012-03-05 22:11:28 +03:00
}
2012-02-14 21:04:45 +03:00
}
2012-03-13 23:33:00 +03:00
break ;
2012-02-14 21:04:45 +03:00
}
if ( cb - > getSelectedHero ( ) )
cb - > recalculatePaths ( ) ;
makeTurnInternal ( ) ;
vstd : : clear_pointer ( makingTurn ) ;
return ;
}
void VCAI : : makeTurnInternal ( )
{
saving = 0 ;
2012-03-11 04:26:11 +03:00
//it looks messy here, but it's better to have armed heroes before attempting realizing goals
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
moveCreaturesToHero ( t ) ;
2012-02-14 21:04:45 +03:00
try
{
striveToGoal ( CGoal ( WIN ) ) ;
2012-03-13 15:47:47 +03:00
for ( auto hg = lockedHeroes . begin ( ) ; hg ! = lockedHeroes . end ( ) ; hg + + ) //continue our goals
{
2012-03-13 23:33:00 +03:00
striveToGoal ( hg - > second ) ;
2012-03-13 15:47:47 +03:00
}
striveToGoal ( CGoal ( BUILD ) ) ; //TODO: smarter building management
2012-02-14 21:04:45 +03:00
}
catch ( boost : : thread_interrupted & e )
{
tlog0 < < " Making turn thread has been interrupted. We'll end without calling endTurn. \n " ;
return ;
}
catch ( std : : exception & e )
{
tlog0 < < " Making turn thread has caught an exception: " < < e . what ( ) < < " \n " ;
}
endTurn ( ) ;
}
bool VCAI : : goVisitObj ( const CGObjectInstance * obj , const CGHeroInstance * h )
{
int3 dst = obj - > visitablePos ( ) ;
BNLOG ( " %s will try to visit %s at (%s) " , h - > name % obj - > hoverName % strFromInt3 ( dst ) ) ;
return moveHeroToTile ( dst , h ) ;
}
2012-03-05 22:11:28 +03:00
void VCAI : : performObjectInteraction ( const CGObjectInstance * obj , const CGHeroInstance * h )
{
switch ( obj - > ID )
{
case Obj : : CREATURE_GENERATOR1 :
recruitCreatures ( dynamic_cast < const CGDwelling * > ( obj ) ) ;
break ;
}
}
2012-02-14 21:04:45 +03:00
void VCAI : : moveCreaturesToHero ( const CGTownInstance * t )
{
2012-03-11 04:26:11 +03:00
if ( t - > visitingHero & & t - > armedGarrison ( ) )
2012-02-14 21:04:45 +03:00
{
2012-03-06 21:49:23 +03:00
pickBestCreatures ( t - > visitingHero , t ) ;
}
}
void VCAI : : pickBestCreatures ( const CArmedInstance * army , const CArmedInstance * source )
{
2012-03-11 04:26:11 +03:00
//TODO - what if source is a hero (the last stack problem) -> it'd good to create a single stack of weakest cre
const CArmedInstance * armies [ ] = { army , source } ;
//we calculate total strength for each creature type available in armies
std : : map < const CCreature * , int > creToPower ;
BOOST_FOREACH ( auto armyPtr , armies )
BOOST_FOREACH ( auto & i , armyPtr - > Slots ( ) )
creToPower [ i . second - > type ] + = i . second - > getPower ( ) ;
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
std : : vector < const CCreature * > bestArmy ; //types that'll be in final dst army
for ( int i = 0 ; i < GameConstants : : ARMY_SIZE ; i + + ) //pick the creatures from which we can get most power, as many as dest can fit
2012-03-06 21:49:23 +03:00
{
2012-03-11 04:26:11 +03:00
typedef const std : : pair < const CCreature * , int > & CrePowerPair ;
auto creIt = boost : : max_element ( creToPower , [ ] ( CrePowerPair lhs , CrePowerPair rhs )
2012-03-06 21:49:23 +03:00
{
2012-03-11 04:26:11 +03:00
return lhs . second < rhs . second ;
} ) ;
bestArmy . push_back ( creIt - > first ) ;
creToPower . erase ( creIt ) ;
if ( creToPower . empty ( ) )
break ;
2012-02-14 21:04:45 +03:00
}
2012-03-11 04:26:11 +03:00
//foreach best type -> iterate over slots in both armies and if it's the appropriate type, send it to the slot where it belongs
for ( int i = 0 ; i < bestArmy . size ( ) ; i + + ) //i-th strongest creature type will go to i-th slot
BOOST_FOREACH ( auto armyPtr , armies )
for ( int j = 0 ; j < GameConstants : : ARMY_SIZE ; j + + )
if ( armyPtr - > getCreature ( j ) = = bestArmy [ i ] & & ( i ! = j | | armyPtr ! = army ) ) //it's a searched creature not in dst slot
cb - > mergeOrSwapStacks ( armyPtr , army , j , i ) ;
//TODO - having now strongest possible army, we may want to think about arranging stacks
2012-02-14 21:04:45 +03:00
}
2012-03-05 22:11:28 +03:00
void VCAI : : recruitCreatures ( const CGDwelling * d )
2012-02-14 21:04:45 +03:00
{
2012-03-05 22:11:28 +03:00
for ( int i = 0 ; i < d - > creatures . size ( ) ; i + + )
2012-02-14 21:04:45 +03:00
{
2012-03-05 22:11:28 +03:00
if ( ! d - > creatures [ i ] . second . size ( ) )
2012-02-14 21:04:45 +03:00
continue ;
2012-03-05 22:11:28 +03:00
int count = d - > creatures [ i ] . first ;
int creID = d - > creatures [ i ] . second . back ( ) ;
2012-02-16 20:10:58 +03:00
// const CCreature *c = VLC->creh->creatures[creID];
2012-02-14 21:04:45 +03:00
// if(containsSavedRes(c->cost))
// continue;
TResources myRes = cb - > getResourceAmount ( ) ;
myRes [ Res : : GOLD ] - = GOLD_RESERVE ;
amin ( count , myRes / VLC - > creh - > creatures [ creID ] - > cost ) ;
if ( count > 0 )
2012-03-05 22:11:28 +03:00
cb - > recruitCreatures ( d , creID , count , i ) ;
2012-02-14 21:04:45 +03:00
}
}
void VCAI : : buildStructure ( const CGTownInstance * t )
{
//TODO make *real* town development system
2012-02-16 20:10:58 +03:00
const int buildings [ ] = { 5 , 11 , 14 , 16 , 0 , 12 , 7 , 8 , 9 , 13 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 ,
2012-02-14 21:04:45 +03:00
39 , 40 , 41 , 42 , 43 , 1 , 2 , 3 , 4 , 17 , 18 , 19 , 21 , 22 , 23 } ;
for ( int i = 0 ; i < ARRAY_COUNT ( buildings ) ; i + + )
{
if ( t - > hasBuilt ( buildings [ i ] ) )
continue ;
const CBuilding * b = VLC - > buildh - > buildings [ t - > subID ] [ buildings [ i ] ] ;
2012-02-16 20:10:58 +03:00
int canBuild = cb - > canBuildStructure ( t , buildings [ i ] ) ;
2012-02-14 21:04:45 +03:00
if ( canBuild = = EBuildingState : : ALLOWED )
{
if ( ! containsSavedRes ( b - > resources ) )
{
BNLOG ( " Player %d will build %s in town of %s at %s " , playerID % b - > Name ( ) % t - > name % t - > pos ) ;
cb - > buildBuilding ( t , buildings [ i ] ) ;
}
break ;
}
else if ( canBuild = = EBuildingState : : NO_RESOURCES )
{
TResources mine = cb - > getResourceAmount ( ) , cost = VLC - > buildh - > buildings [ t - > subID ] [ buildings [ i ] ] - > resources ,
income = estimateIncome ( ) ;
for ( int i = 0 ; i < GameConstants : : RESOURCE_QUANTITY ; i + + )
{
int diff = mine [ i ] - cost [ i ] + income [ i ] ;
if ( diff < 0 )
saving [ i ] = 1 ;
}
continue ;
}
}
}
bool isSafeToVisit ( const CGHeroInstance * h , crint3 tile )
{
const ui64 heroStrength = h - > getTotalStrength ( ) ,
2012-03-04 13:43:46 +03:00
dangerStrength = evaluateDanger ( tile , h ) ;
2012-02-14 21:04:45 +03:00
if ( dangerStrength )
{
if ( heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength )
{
BNLOG ( " It's, safe for %s to visit tile %s " , h - > name % tile ) ;
return true ;
}
else
return false ;
}
2012-02-16 20:10:58 +03:00
2012-02-14 21:04:45 +03:00
return true ; //there's no danger
}
std : : vector < const CGObjectInstance * > VCAI : : getPossibleDestinations ( const CGHeroInstance * h )
{
validateVisitableObjs ( ) ;
std : : vector < const CGObjectInstance * > possibleDestinations ;
BOOST_FOREACH ( const CGObjectInstance * obj , visitableObjs )
2012-03-05 22:11:28 +03:00
{
2012-03-14 16:02:38 +03:00
if ( cb - > getPathInfo ( obj - > visitablePos ( ) ) - > reachable ( ) & & ! obj - > wasVisited ( playerID ) & &
2012-03-05 22:11:28 +03:00
( obj - > tempOwner ! = playerID | | isWeeklyRevisitable ( obj ) ) ) //flag or get weekly resources / creatures
2012-02-14 21:04:45 +03:00
possibleDestinations . push_back ( obj ) ;
2012-03-05 22:11:28 +03:00
}
2012-02-14 21:04:45 +03:00
boost : : sort ( possibleDestinations , isCloser ) ;
possibleDestinations . erase ( boost : : remove_if ( possibleDestinations , [ & ] ( const CGObjectInstance * obj ) - > bool
{
if ( vstd : : contains ( alreadyVisited , obj ) )
return true ;
if ( ! isSafeToVisit ( h , obj - > visitablePos ( ) ) )
return true ;
2012-03-14 16:02:38 +03:00
if ( ! shouldVisit ( h , obj ) )
return true ;
2012-03-29 21:26:06 +03:00
if ( vstd : : contains ( reservedObjs , obj ) ) //does checking for our own reserved objects make sense? here?
return true ;
2012-03-14 16:02:38 +03:00
2012-02-14 21:04:45 +03:00
return false ;
} ) , possibleDestinations . end ( ) ) ;
return possibleDestinations ;
}
void VCAI : : wander ( const CGHeroInstance * h )
{
while ( 1 )
{
2012-03-29 21:26:06 +03:00
std : : vector < const CGObjectInstance * > dests ( reservedHeroesMap [ h ] . begin ( ) , reservedHeroesMap [ h ] . end ( ) ) ; //copy constructor
if ( ! dests . size ( ) )
dests = getPossibleDestinations ( h ) ;
2012-03-13 23:33:00 +03:00
if ( ! dests . size ( ) )
2012-02-14 21:04:45 +03:00
{
2012-03-29 21:26:06 +03:00
auto compareReinforcements = [ h ] ( const CGTownInstance * lhs , const CGTownInstance * rhs ) - > bool
{
return howManyReinforcementsCanGet ( h , lhs ) < howManyReinforcementsCanGet ( h , rhs ) ;
} ;
std : : vector < const CGTownInstance * > townsReachable ;
std : : vector < const CGTownInstance * > townsNotReachable ;
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
{
if ( ! t - > visitingHero & & howManyReinforcementsCanGet ( h , t ) & & ! vstd : : contains ( townVisitsThisWeek [ h ] , t ) )
{
if ( isReachable ( t ) )
townsReachable . push_back ( t ) ;
else
townsNotReachable . push_back ( t ) ;
}
}
if ( townsReachable . size ( ) )
{
boost : : sort ( townsReachable , compareReinforcements ) ;
dests . push_back ( townsReachable . back ( ) ) ;
}
else if ( townsNotReachable . size ( ) )
{
boost : : sort ( townsNotReachable , compareReinforcements ) ;
//TODO pick the truly best
const CGTownInstance * t = townsNotReachable . back ( ) ;
BNLOG ( " %s can't reach any town, we'll try to make our way to %s at %s " , h - > name % t - > name % t - > visitablePos ( ) ) ;
int3 pos1 = h - > pos ;
striveToGoal ( CGoal ( CLEAR_WAY_TO ) . settile ( t - > visitablePos ( ) ) . sethero ( h ) ) ;
if ( pos1 = = h - > pos & & h = = primaryHero ( ) ) //hero can't move
{
if ( cb - > getResourceAmount ( Res : : GOLD ) > = HERO_GOLD_COST & & cb - > getHeroesInfo ( ) . size ( ) < ALLOWED_ROAMING_HEROES & & cb - > getAvailableHeroes ( t ) . size ( ) )
recruitHero ( t ) ;
}
break ;
}
else if ( cb - > getResourceAmount ( Res : : GOLD ) > = HERO_GOLD_COST )
{
std : : vector < const CGTownInstance * > towns = cb - > getTownsInfo ( ) ;
erase_if ( towns , [ ] ( const CGTownInstance * t ) - > bool
{
BOOST_FOREACH ( const CGHeroInstance * h , cb - > getHeroesInfo ( ) )
if ( ! t - > getArmyStrength ( ) | | howManyReinforcementsCanGet ( h , t ) )
return true ;
return false ;
} ) ;
boost : : sort ( towns , compareArmyStrength ) ;
if ( towns . size ( ) )
recruitHero ( towns . back ( ) ) ;
break ;
}
else
{
PNLOG ( " Nowhere more to go... \n " ) ;
break ;
}
2012-02-14 21:04:45 +03:00
}
2012-03-13 23:33:00 +03:00
const CGObjectInstance * obj = dests . front ( ) ;
if ( ! goVisitObj ( obj , h ) )
2012-02-14 21:04:45 +03:00
{
BNLOG ( " Hero %s apparently used all MPs (%d left) \n " , h - > name % h - > movement ) ;
2012-03-29 21:26:06 +03:00
reserveObject ( h , obj ) ; //reserve that object - we predict it will be reached soon
2012-03-13 23:33:00 +03:00
setGoal ( h , CGoal ( VISIT_TILE ) . sethero ( h ) . settile ( obj - > visitablePos ( ) ) ) ;
2012-02-14 21:04:45 +03:00
break ;
}
if ( h - > visitedTown )
{
townVisitsThisWeek [ h ] . push_back ( h - > visitedTown ) ;
buildArmyIn ( h - > visitedTown ) ;
break ;
}
}
}
2012-03-13 15:47:47 +03:00
void VCAI : : setGoal ( const CGHeroInstance * h , const CGoal goal )
{ //TODO: check for presence?
lockedHeroes [ h ] = goal ;
}
void VCAI : : setGoal ( const CGHeroInstance * h , EGoals goalType )
{
lockedHeroes [ h ] = CGoal ( goalType ) ;
}
2012-02-14 21:04:45 +03:00
void VCAI : : battleStart ( const CCreatureSet * army1 , const CCreatureSet * army2 , int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 , bool side )
{
assert ( playerID > GameConstants : : PLAYER_LIMIT | | status . getBattle ( ) = = UPCOMING_BATTLE ) ;
status . setBattle ( ONGOING_BATTLE ) ;
const TerrainTile * t = myCb - > getTile ( tile ) ; //may be NULL in some very are cases -> eg. visited monolith and fighting with an enemy at the FoW covered exit
battlename = boost : : str ( boost : : format ( " battle of %s attacking %s at %s " ) % ( hero1 ? hero1 - > name : " a army " ) % ( t ? t - > visitableObjects . back ( ) - > hoverName : " unknown enemy " ) % tile ) ;
CAdventureAI : : battleStart ( army1 , army2 , tile , hero1 , hero2 , side ) ;
}
void VCAI : : battleEnd ( const BattleResult * br )
{
assert ( status . getBattle ( ) = = ONGOING_BATTLE ) ;
status . setBattle ( ENDING_BATTLE ) ;
bool won = br - > winner = = myCb - > battleGetMySide ( ) ;
BNLOG ( " Player %d: I %s the %s! " , playerID % ( won ? " won " : " lost " ) % battlename ) ;
battlename . clear ( ) ;
CAdventureAI : : battleEnd ( br ) ;
}
void VCAI : : waitTillFree ( )
{
2012-02-20 00:03:43 +03:00
auto unlock = vstd : : makeUnlockSharedGuard ( cb - > getGsMutex ( ) ) ;
2012-02-14 21:04:45 +03:00
status . waitTillFree ( ) ;
}
2012-03-14 16:02:38 +03:00
void VCAI : : markObjectVisited ( const CGObjectInstance * obj )
{
if ( dynamic_cast < const CGVisitableOPH * > ( obj ) | | //we may want to wisit it with another hero
dynamic_cast < const CGBonusingObject * > ( obj ) | | //or another time
( obj - > ID = = Obj : : MONSTER ) )
return ;
alreadyVisited . push_back ( obj ) ;
}
2012-03-29 21:26:06 +03:00
void VCAI : : reserveObject ( const CGHeroInstance * h , const CGObjectInstance * obj )
{
reservedObjs . push_back ( obj ) ;
reservedHeroesMap [ h ] . insert ( obj ) ;
}
2012-02-14 21:04:45 +03:00
void VCAI : : validateVisitableObjs ( )
{
std : : vector < const CGObjectInstance * > hlp ;
retreiveVisitableObjs ( hlp , true ) ;
start :
BOOST_FOREACH ( const CGObjectInstance * obj , visitableObjs )
if ( ! vstd : : contains ( hlp , obj ) )
{
tlog1 < < helperObjInfo [ obj ] . name < < " at " < < helperObjInfo [ obj ] . pos < < " shouldn't be on list! \n " ;
remove_if_present ( visitableObjs , obj ) ;
goto start ;
}
}
void VCAI : : retreiveVisitableObjs ( std : : vector < const CGObjectInstance * > & out , bool includeOwned /*= false*/ ) const
{
for ( int i = 0 ; i < cb - > getMapSize ( ) . x ; i + + )
for ( int j = 0 ; j < cb - > getMapSize ( ) . y ; j + + )
for ( int k = 0 ; k < cb - > getMapSize ( ) . z ; k + + )
if ( const TerrainTile * t = cb - > getTile ( int3 ( i , j , k ) , false ) )
2012-02-16 20:10:58 +03:00
{
2012-02-14 21:04:45 +03:00
BOOST_FOREACH ( const CGObjectInstance * obj , t - > visitableObjects )
2012-02-16 20:10:58 +03:00
{
2012-02-14 21:04:45 +03:00
if ( includeOwned | | obj - > tempOwner ! = playerID )
out . push_back ( obj ) ;
2012-02-16 20:10:58 +03:00
}
}
2012-02-14 21:04:45 +03:00
}
std : : vector < const CGObjectInstance * > VCAI : : getFlaggedObjects ( ) const
{
std : : vector < const CGObjectInstance * > ret ;
retreiveVisitableObjs ( ret , true ) ;
erase_if ( ret , [ ] ( const CGObjectInstance * obj )
{
return obj - > tempOwner ! = ai - > playerID ;
} ) ;
return ret ;
}
void VCAI : : addVisitableObj ( const CGObjectInstance * obj )
{
visitableObjs . push_back ( obj ) ;
helperObjInfo [ obj ] = ObjInfo ( obj ) ;
}
const CGObjectInstance * VCAI : : lookForArt ( int aid ) const
{
BOOST_FOREACH ( const CGObjectInstance * obj , ai - > visitableObjs )
{
if ( obj - > ID = = 5 & & obj - > subID = = aid )
return obj ;
}
return NULL ;
//TODO what if more than one artifact is available? return them all or some slection criteria
}
bool VCAI : : isAccessible ( const int3 & pos )
{
//TODO precalculate for speed
BOOST_FOREACH ( const CGHeroInstance * h , cb - > getHeroesInfo ( ) )
{
if ( isAccessibleForHero ( pos , h ) )
return true ;
}
return false ;
}
const CGHeroInstance * VCAI : : getHeroWithGrail ( ) const
{
BOOST_FOREACH ( const CGHeroInstance * h , cb - > getHeroesInfo ( ) )
if ( h - > hasArt ( 2 ) ) //grail
return h ;
return NULL ;
}
const CGObjectInstance * VCAI : : getUnvisitedObj ( const boost : : function < bool ( const CGObjectInstance * ) > & predicate )
{
//TODO smarter definition of unvisited
BOOST_FOREACH ( const CGObjectInstance * obj , visitableObjs )
if ( predicate ( obj ) & & ! vstd : : contains ( alreadyVisited , obj ) )
return obj ;
return NULL ;
}
bool VCAI : : isAccessibleForHero ( const int3 & pos , const CGHeroInstance * h ) const
{
cb - > setSelection ( h ) ;
return cb - > getPathInfo ( pos ) - > reachable ( ) ;
}
class cannotFulfillGoalException : public std : : exception
{
std : : string msg ;
public :
explicit cannotFulfillGoalException ( crstring _Message ) : msg ( _Message )
{
}
2012-02-16 20:10:58 +03:00
virtual ~ cannotFulfillGoalException ( ) throw ( )
{
} ;
const char * what ( ) const throw ( ) OVERRIDE
2012-02-14 21:04:45 +03:00
{
return msg . c_str ( ) ;
}
} ;
bool VCAI : : moveHeroToTile ( int3 dst , const CGHeroInstance * h )
{
2012-03-05 22:11:28 +03:00
visitedObject = NULL ;
2012-02-14 21:04:45 +03:00
int3 startHpos = h - > visitablePos ( ) ;
bool ret = false ;
if ( startHpos = = dst )
{
assert ( cb - > getTile ( dst ) - > visitableObjects . size ( ) > 1 ) ; //there's no point in revisiting tile where there is no visitable object
cb - > moveHero ( h , CGHeroInstance : : convertPosition ( dst , true ) ) ;
waitTillFree ( ) ; //movement may cause battle or blocking dialog
ret = true ;
}
else
{
CGPath path ;
cb - > getPath2 ( dst , path ) ;
if ( path . nodes . empty ( ) )
{
tlog1 < < " Hero " < < h - > name < < " cannot reach " < < dst < < std : : endl ;
throw std : : runtime_error ( " Wrong move order! " ) ;
}
int i = path . nodes . size ( ) - 1 ;
for ( ; i > 0 ; i - - )
{
//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
if ( path . nodes [ i - 1 ] . turns )
{
2012-03-13 15:47:47 +03:00
//blockedHeroes.insert(h); //to avoid attempts of moving heroes with very little MPs
2012-02-14 21:04:45 +03:00
break ;
}
int3 endpos = path . nodes [ i - 1 ] . coord ;
if ( endpos = = h - > visitablePos ( ) )
continue ;
// if(i > 1)
// {
// int3 afterEndPos = path.nodes[i-2].coord;
// if(afterEndPos.z != endpos.z)
2012-02-16 20:10:58 +03:00
//
2012-02-14 21:04:45 +03:00
// }
//tlog0 << "Moving " << h->name << " from " << h->getPosition() << " to " << endpos << std::endl;
cb - > moveHero ( h , CGHeroInstance : : convertPosition ( endpos , true ) ) ;
waitTillFree ( ) ; //movement may cause battle or blocking dialog
2012-02-22 16:41:27 +03:00
boost : : this_thread : : interruption_point ( ) ;
2012-03-29 21:26:06 +03:00
if ( h - > tempOwner ! = playerID ) //we lost hero - remove all tasks assigned to him/her
2012-03-13 15:47:47 +03:00
{
remove_if_present ( lockedHeroes , h ) ;
2012-03-29 21:26:06 +03:00
BOOST_FOREACH ( auto obj , reservedHeroesMap [ h ] )
{
remove_if_present ( reservedObjs , obj ) ; //unreserve all objects for that hero
}
remove_if_present ( reservedHeroesMap , h ) ;
2012-03-26 01:46:14 +03:00
throw std : : runtime_error ( " Hero was lost! " ) ; //we need to throw, otherwise hero will be assigned to sth again
2012-02-14 21:04:45 +03:00
break ;
2012-03-13 15:47:47 +03:00
}
2012-02-14 21:04:45 +03:00
}
ret = ! i ;
}
2012-03-05 22:11:28 +03:00
if ( visitedObject ) //we step into something interesting
2012-03-29 21:26:06 +03:00
{
2012-03-05 22:11:28 +03:00
performObjectInteraction ( visitedObject , h ) ;
2012-03-29 21:26:06 +03:00
}
2012-02-14 21:04:45 +03:00
if ( h - > tempOwner = = playerID ) //lost hero after last move
cb - > recalculatePaths ( ) ;
BNLOG ( " Hero %s moved from %s to %s " , h - > name % startHpos % h - > visitablePos ( ) ) ;
return ret ;
}
int howManyTilesWillBeDiscovered ( const int3 & pos , int radious )
{
int ret = 0 ;
for ( int x = pos . x - radious ; x < = pos . x + radious ; x + + )
{
for ( int y = pos . y - radious ; y < = pos . y + radious ; y + + )
{
int3 npos = int3 ( x , y , pos . z ) ;
if ( cb - > isInTheMap ( npos ) & & pos . dist2d ( npos ) - 0.5 < radious & & ! cb - > isVisible ( npos ) )
{
ret + + ;
}
}
}
return ret ;
}
int howManyTilesWillBeDiscovered ( int radious , int3 pos , crint3 dir )
{
return howManyTilesWillBeDiscovered ( pos + dir , radious ) ;
}
void getVisibleNeighbours ( const std : : vector < int3 > & tiles , std : : vector < int3 > & out )
{
BOOST_FOREACH ( const int3 & tile , tiles )
{
foreach_neighbour ( tile , [ & ] ( int3 neighbour )
{
if ( cb - > isVisible ( neighbour ) )
out . push_back ( neighbour ) ;
} ) ;
}
}
void VCAI : : tryRealize ( CGoal g )
{
BNLOG ( " Attempting realizing goal with code %d " , g . goalType ) ;
switch ( g . goalType )
{
case EXPLORE :
{
assert ( 0 ) ; //this goal is not elementar!
}
break ;
case RECRUIT_HERO :
{
if ( const CGTownInstance * t = findTownWithTavern ( ) )
{
//TODO co jesli nie ma dostepnego bohatera?
//TODO jezeli miasto jest zablokowane, sprobowac oczyscic wejscie
cb - > recruitHero ( t , cb - > getAvailableHeroes ( t ) [ 0 ] ) ;
}
//TODO karkolomna alternatywa - tawerna na mapie przygod lub wiezienie (nie wiem, czy warto?)
}
break ;
case VISIT_TILE :
{
if ( ! g . hero - > movement )
throw cannotFulfillGoalException ( " Cannot visit tile: hero is out of MPs! " ) ;
if ( ! g . isBlockedBorderGate ( g . tile ) )
2012-03-05 22:11:28 +03:00
{
2012-03-13 15:47:47 +03:00
if ( ai - > moveHeroToTile ( g . tile , g . hero ) ) ;
setGoal ( g . hero , INVALID ) ; //this hero reached target and no goal
2012-03-05 22:11:28 +03:00
}
2012-02-14 21:04:45 +03:00
else
throw cannotFulfillGoalException ( " There's a blocked gate! " ) ;
}
break ;
case BUILD_STRUCTURE :
{
const CGTownInstance * t = g . town ;
if ( ! t & & g . hero )
t = g . hero - > visitedTown ;
if ( ! t )
{
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
{
switch ( cb - > canBuildStructure ( t , g . bid ) )
{
case EBuildingState : : ALLOWED :
cb - > buildBuilding ( t , g . bid ) ;
return ;
default :
break ;
}
}
}
else if ( cb - > canBuildStructure ( t , g . bid ) = = EBuildingState : : ALLOWED )
{
cb - > buildBuilding ( t , g . bid ) ;
return ;
}
throw cannotFulfillGoalException ( " Cannot build a given structure! " ) ;
}
break ;
case DIG_AT_TILE :
{
assert ( g . hero - > visitablePos ( ) = = g . tile ) ;
2012-03-13 15:47:47 +03:00
if ( g . hero - > diggingStatus ( ) = = CGHeroInstance : : CAN_DIG )
{
cb - > dig ( g . hero ) ;
setGoal ( g . hero , INVALID ) ; // finished digging
}
2012-02-14 21:04:45 +03:00
else
{
2012-03-13 15:47:47 +03:00
ai - > lockedHeroes [ g . hero ] = g ; //hero who tries to dig shouldn't do anything else
2012-02-14 21:04:45 +03:00
throw cannotFulfillGoalException ( " A hero can't dig! \n " ) ;
}
}
break ;
2012-02-16 20:10:58 +03:00
2012-02-14 21:04:45 +03:00
case COLLECT_RES :
if ( const CGObjectInstance * obj = cb - > getObj ( g . objid , false ) )
{
if ( const IMarket * m = IMarket : : castFrom ( obj , false ) )
{
for ( int i = 0 ; i < ACTUAL_RESOURCE_COUNT ; i + + )
{
if ( i = = g . resID ) continue ;
int toGive , toGet ;
m - > getOffer ( i , g . resID , toGive , toGet , EMarketMode : : RESOURCE_RESOURCE ) ;
toGive = toGive * ( cb - > getResourceAmount ( i ) / toGive ) ;
cb - > trade ( obj , EMarketMode : : RESOURCE_RESOURCE , i , g . resID , toGive ) ;
if ( cb - > getResourceAmount ( g . resID ) > = g . value )
return ;
}
}
else
{
throw cannotFulfillGoalException ( " I don't know how to use this object to raise resources! " ) ;
}
}
else
{
saving [ g . resID ] = 1 ;
throw cannotFulfillGoalException ( " No object that could be used to raise resources! " ) ;
}
case CONQUER :
2012-03-13 15:47:47 +03:00
case GATHER_ARMY :
case BOOST_HERO :
2012-02-14 21:04:45 +03:00
// TODO: conquer??
throw cannotFulfillGoalException ( " I don't know how to fulfill this! " ) ;
case BUILD :
performTypicalActions ( ) ;
throw cannotFulfillGoalException ( " BUILD has been realized as much as possible. " ) ;
case INVALID :
throw cannotFulfillGoalException ( " I don't know how to fulfill this! " ) ;
default :
2012-03-13 15:47:47 +03:00
throw cannotFulfillGoalException ( " Unknown type of goal ! " ) ;
2012-02-14 21:04:45 +03:00
}
}
const CGTownInstance * VCAI : : findTownWithTavern ( ) const
{
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
2012-03-13 15:47:47 +03:00
if ( vstd : : contains ( t - > builtBuildings , EBuilding : : TAVERN ) & & ! t - > visitingHero )
2012-02-14 21:04:45 +03:00
return t ;
return NULL ;
}
std : : vector < const CGHeroInstance * > VCAI : : getUnblockedHeroes ( ) const
{
std : : vector < const CGHeroInstance * > ret = cb - > getHeroesInfo ( ) ;
2012-03-13 15:47:47 +03:00
BOOST_FOREACH ( auto h , lockedHeroes )
{
if ( ! h . second . invalid ( ) ) //we can use heroes without valid goal
remove_if_present ( ret , h . first ) ;
}
2012-02-14 21:04:45 +03:00
return ret ;
}
const CGHeroInstance * VCAI : : primaryHero ( ) const
{
auto hs = cb - > getHeroesInfo ( ) ;
boost : : sort ( hs , compareHeroStrength ) ;
if ( hs . empty ( ) )
return NULL ;
return hs . back ( ) ;
}
void VCAI : : endTurn ( )
{
tlog4 < < " Player " < < playerID < < " ends turn \n " ;
if ( ! status . haveTurn ( ) )
{
tlog1 < < " Not having turn at the end of turn??? \n " ;
}
2012-02-16 20:10:58 +03:00
do
2012-02-14 21:04:45 +03:00
{
cb - > endTurn ( ) ;
} while ( status . haveTurn ( ) ) ; //for some reasons, our request may fail -> stop requesting end of turn only after we've received a confirmation that it's over
tlog4 < < " Player " < < playerID < < " ended turn \n " ;
}
void VCAI : : striveToGoal ( const CGoal & ultimateGoal )
{
2012-03-13 23:33:00 +03:00
if ( ultimateGoal . invalid ( ) )
return ;
2012-02-14 21:04:45 +03:00
while ( 1 )
{
CGoal goal = ultimateGoal ;
BNLOG ( " Striving to goal of type %d " , ultimateGoal . goalType ) ;
2012-03-13 15:47:47 +03:00
int maxGoals = 100 ; //preventing deadlock for mutually dependent goals
while ( ! goal . isElementar & & maxGoals )
2012-02-14 21:04:45 +03:00
{
INDENT ;
2012-03-13 23:33:00 +03:00
BNLOG ( " Considering goal %s " , goalName ( goal . goalType ) ) ;
2012-02-14 21:04:45 +03:00
try
{
boost : : this_thread : : interruption_point ( ) ;
goal = goal . whatToDoToAchieve ( ) ;
2012-03-13 15:47:47 +03:00
- - maxGoals ;
2012-02-14 21:04:45 +03:00
}
catch ( std : : exception & e )
{
2012-03-13 23:33:00 +03:00
BNLOG ( " Goal %s decomposition failed: %s " , goalName ( goal . goalType ) % e . what ( ) ) ;
2012-02-14 21:04:45 +03:00
return ;
}
}
try
{
boost : : this_thread : : interruption_point ( ) ;
2012-03-13 15:47:47 +03:00
if ( goal . hero ) //lock this hero to fulfill ultimate goal
{
if ( maxGoals )
{
setGoal ( goal . hero , goal ) ;
}
else
{
setGoal ( goal . hero , INVALID ) ; // we seemingly don't know what to do with hero
}
}
2012-02-14 21:04:45 +03:00
tryRealize ( goal ) ;
boost : : this_thread : : interruption_point ( ) ;
}
catch ( boost : : thread_interrupted & e )
{
BNLOG ( " Player %d: Making turn thread received an interruption! " , playerID ) ;
throw ; //rethrow, we want to truly end this thread
}
catch ( std : : exception & e )
{
BNLOG ( " Failed to realize subgoal of type %d (greater goal type was %d), I will stop. " , goal . goalType % ultimateGoal . goalType ) ;
BNLOG ( " The error message was: %s " , e . what ( ) ) ;
break ;
}
}
}
void VCAI : : performTypicalActions ( )
{
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
{
BNLOG ( " Looking into %s " , t - > name ) ;
buildStructure ( t ) ;
buildArmyIn ( t ) ;
if ( ! ai - > primaryHero ( ) | |
2012-02-16 20:10:58 +03:00
( t - > getArmyStrength ( ) > ai - > primaryHero ( ) - > getArmyStrength ( ) * 2 & & ! isAccessibleForHero ( t - > visitablePos ( ) , ai - > primaryHero ( ) ) ) )
2012-02-14 21:04:45 +03:00
{
recruitHero ( t ) ;
buildArmyIn ( t ) ;
}
}
BOOST_FOREACH ( const CGHeroInstance * h , getUnblockedHeroes ( ) )
{
BNLOG ( " Looking into %s, MP=%d " , h - > name . c_str ( ) % h - > movement ) ;
INDENT ;
makePossibleUpgrades ( h ) ;
cb - > setSelection ( h ) ;
2012-03-26 01:46:14 +03:00
try
{
wander ( h ) ;
}
catch ( std : : exception & e )
{
BNLOG ( " Cannot use this hero anymore, received exception: %s " , e . what ( ) ) ;
continue ;
}
2012-02-14 21:04:45 +03:00
}
}
void VCAI : : buildArmyIn ( const CGTownInstance * t )
{
makePossibleUpgrades ( t - > visitingHero ) ;
makePossibleUpgrades ( t ) ;
recruitCreatures ( t ) ;
moveCreaturesToHero ( t ) ;
}
int3 VCAI : : explorationBestNeighbour ( int3 hpos , int radius , const CGHeroInstance * h )
{
TimeCheck tc ( " looking for best exploration neighbour " ) ;
std : : map < int3 , int > dstToRevealedTiles ;
BOOST_FOREACH ( crint3 dir , dirs )
if ( cb - > isInTheMap ( hpos + dir ) )
dstToRevealedTiles [ hpos + dir ] = howManyTilesWillBeDiscovered ( radius , hpos , dir ) * isSafeToVisit ( h , hpos + dir ) ;
auto best = dstToRevealedTiles . begin ( ) ;
best - > second * = cb - > getPathInfo ( best - > first ) - > reachable ( ) ;
best - > second * = cb - > getPathInfo ( best - > first ) - > accessible = = CGPathNode : : ACCESSIBLE ;
for ( auto i = dstToRevealedTiles . begin ( ) ; i ! = dstToRevealedTiles . end ( ) ; i + + )
{
const CGPathNode * pn = cb - > getPathInfo ( i - > first ) ;
2012-02-16 20:10:58 +03:00
//const TerrainTile *t = cb->getTile(i->first);
2012-02-14 21:04:45 +03:00
if ( best - > second < i - > second & & i - > second & & pn - > reachable ( ) & & pn - > accessible = = CGPathNode : : ACCESSIBLE )
best = i ;
}
if ( best - > second )
return best - > first ;
throw cannotFulfillGoalException ( " No neighbour will bring new discoveries! " ) ;
}
int3 VCAI : : explorationNewPoint ( int radius , const CGHeroInstance * h , std : : vector < std : : vector < int3 > > & tiles )
{
TimeCheck tc ( " looking for new exploration point " ) ;
tlog0 < < " Looking for an another place for exploration... \n " ;
tiles . resize ( radius ) ;
foreach_tile_pos ( [ & ] ( const int3 & pos )
{
if ( ! cb - > isVisible ( pos ) )
tiles [ 0 ] . push_back ( pos ) ;
} ) ;
for ( int i = 1 ; i < radius ; i + + )
{
getVisibleNeighbours ( tiles [ i - 1 ] , tiles [ i ] ) ;
removeDuplicates ( tiles [ i ] ) ;
BOOST_FOREACH ( const int3 & tile , tiles [ i ] )
{
if ( cb - > getPathInfo ( tile ) - > reachable ( ) & & isSafeToVisit ( h , tile ) & & howManyTilesWillBeDiscovered ( tile , radius ) )
{
return tile ;
}
}
}
throw cannotFulfillGoalException ( " No accessible tile will bring discoveries! " ) ;
}
TResources VCAI : : estimateIncome ( ) const
{
TResources ret ;
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
{
ret [ Res : : GOLD ] + = t - > dailyIncome ( ) ;
//TODO duplikuje newturn
if ( t - > hasBuilt ( EBuilding : : RESOURCE_SILO ) ) //there is resource silo
{
if ( t - > town - > primaryRes = = 127 ) //we'll give wood and ore
{
ret [ Res : : WOOD ] + + ;
ret [ Res : : ORE ] + + ;
}
else
{
ret [ t - > town - > primaryRes ] + + ;
}
}
}
BOOST_FOREACH ( const CGObjectInstance * obj , getFlaggedObjects ( ) )
{
if ( obj - > ID = = Obj : : MINE )
{
switch ( obj - > subID )
{
case Res : : WOOD :
case Res : : ORE :
ret [ obj - > subID ] + = WOOD_ORE_MINE_PRODUCTION ;
break ;
case Res : : GOLD :
case 7 : //abandoned mine -> also gold
ret [ Res : : GOLD ] + = GOLD_MINE_PRODUCTION ;
break ;
default :
ret [ obj - > subID ] + = RESOURCE_MINE_PRODUCTION ;
break ;
}
}
}
return ret ;
}
bool VCAI : : containsSavedRes ( const TResources & cost ) const
{
for ( int i = 0 ; i < GameConstants : : RESOURCE_QUANTITY ; i + + )
{
if ( saving [ i ] & & cost [ i ] )
return true ;
}
return false ;
}
void VCAI : : recruitHero ( const CGTownInstance * t )
{
BNLOG ( " Trying to recruit a hero in %s at %s " , t - > name % t - > visitablePos ( ) )
cb - > recruitHero ( t , cb - > getAvailableHeroes ( t ) . front ( ) ) ;
}
2012-02-22 16:41:27 +03:00
void VCAI : : finish ( )
{
if ( makingTurn )
makingTurn - > interrupt ( ) ;
}
2012-03-26 01:46:14 +03:00
void VCAI : : requestActionASAP ( boost : : function < void ( ) > whatToDo )
{
boost : : barrier b ( 2 ) ;
boost : : thread newThread ( [ & b , this , whatToDo ] ( )
{
setThreadName ( - 1 , " VCAI::requestActionASAP::helper " ) ;
SET_GLOBAL_STATE ( this ) ;
boost : : shared_lock < boost : : shared_mutex > gsLock ( cb - > getGsMutex ( ) ) ;
b . wait ( ) ;
whatToDo ( ) ;
} ) ;
b . wait ( ) ;
}
2012-02-14 21:04:45 +03:00
AIStatus : : AIStatus ( )
{
battle = NO_BATTLE ;
remainingQueries = 0 ;
havingTurn = false ;
}
AIStatus : : ~ AIStatus ( )
{
}
void AIStatus : : setBattle ( BattleState BS )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
battle = BS ;
cv . notify_all ( ) ;
}
BattleState AIStatus : : getBattle ( )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
return battle ;
}
void AIStatus : : addQueries ( int val )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
remainingQueries + = val ;
BNLOG ( " Changing count of queries by %d, to a total of %d " , val % remainingQueries ) ;
assert ( remainingQueries > = 0 ) ;
cv . notify_all ( ) ;
}
void AIStatus : : addQuery ( )
{
addQueries ( 1 ) ;
}
void AIStatus : : removeQuery ( )
{
addQueries ( - 1 ) ;
}
int AIStatus : : getQueriesCount ( )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
return remainingQueries ;
}
void AIStatus : : startedTurn ( )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
havingTurn = true ;
cv . notify_all ( ) ;
}
void AIStatus : : madeTurn ( )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
havingTurn = false ;
cv . notify_all ( ) ;
}
void AIStatus : : waitTillFree ( )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
while ( battle ! = NO_BATTLE | | remainingQueries )
cv . wait ( lock ) ;
}
bool AIStatus : : haveTurn ( )
{
boost : : unique_lock < boost : : mutex > lock ( mx ) ;
return havingTurn ;
}
int3 whereToExplore ( const CGHeroInstance * h )
{
//TODO it's stupid and ineffective, write sth better
cb - > setSelection ( h ) ;
int radius = h - > getSightRadious ( ) ;
int3 hpos = h - > visitablePos ( ) ;
//look for nearby objs -> visit them if they're close enouh
const int DIST_LIMIT = 3 ;
std : : vector < const CGObjectInstance * > nearbyVisitableObjs ;
BOOST_FOREACH ( const CGObjectInstance * obj , ai - > getPossibleDestinations ( h ) )
{
int3 op = obj - > visitablePos ( ) ;
CGPath p ;
cb - > getPath2 ( op , p ) ;
if ( p . nodes . size ( ) & & p . endPos ( ) = = op & & p . nodes . size ( ) < = DIST_LIMIT )
nearbyVisitableObjs . push_back ( obj ) ;
}
boost : : sort ( nearbyVisitableObjs , isCloser ) ;
if ( nearbyVisitableObjs . size ( ) )
return nearbyVisitableObjs . back ( ) - > visitablePos ( ) ;
try
{
return ai - > explorationBestNeighbour ( hpos , radius , h ) ;
}
catch ( cannotFulfillGoalException & e )
{
2012-02-16 20:10:58 +03:00
std : : vector < std : : vector < int3 > > tiles ; //tiles[distance_to_fow], metryka taksówkowa
2012-02-14 21:04:45 +03:00
try
{
return ai - > explorationNewPoint ( radius , h , tiles ) ;
}
catch ( cannotFulfillGoalException & e )
{
std : : map < int , std : : vector < int3 > > profits ;
{
TimeCheck tc ( " Evaluating exploration possibilities " ) ;
tiles [ 0 ] . clear ( ) ; //we can't reach FoW anyway
BOOST_FOREACH ( auto & vt , tiles )
BOOST_FOREACH ( auto & tile , vt )
profits [ howManyTilesWillBeDiscovered ( tile , radius ) ] . push_back ( tile ) ;
}
if ( profits . empty ( ) )
throw cannotFulfillGoalException ( " Cannot explore - no possible ways found! " ) ;
auto bestDest = profits . end ( ) ;
bestDest - - ;
return bestDest - > second . front ( ) ; //TODO which is the real best tile?
}
}
}
TSubgoal CGoal : : whatToDoToAchieve ( )
{
switch ( goalType )
{
case WIN :
{
const CVictoryCondition & vc = cb - > getMapHeader ( ) - > victoryCondition ;
EVictoryConditionType : : EVictoryConditionType cond = vc . condition ;
2012-02-16 20:10:58 +03:00
2012-02-14 21:04:45 +03:00
if ( ! vc . appliesToAI )
{
//TODO deduce victory from human loss condition
cond = EVictoryConditionType : : WINSTANDARD ;
}
switch ( cond )
{
case EVictoryConditionType : : ARTIFACT :
return CGoal ( GET_ART_TYPE ) . setaid ( vc . ID ) ;
case EVictoryConditionType : : BEATHERO :
return CGoal ( GET_OBJ ) . setobjid ( vc . ID ) ;
case EVictoryConditionType : : BEATMONSTER :
return CGoal ( GET_OBJ ) . setobjid ( vc . ID ) ;
case EVictoryConditionType : : BUILDCITY :
//TODO build castle/capitol
break ;
case EVictoryConditionType : : BUILDGRAIL :
{
if ( const CGHeroInstance * h = ai - > getHeroWithGrail ( ) )
{
//hero is in a town that can host Grail
if ( h - > visitedTown & & ! vstd : : contains ( h - > visitedTown - > forbiddenBuildings , EBuilding : : GRAIL ) )
{
const CGTownInstance * t = h - > visitedTown ;
return CGoal ( BUILD_STRUCTURE ) . setbid ( EBuilding : : GRAIL ) . settown ( t ) ;
}
else
{
auto towns = cb - > getTownsInfo ( ) ;
2012-02-16 20:10:58 +03:00
towns . erase ( boost : : remove_if ( towns ,
[ ] ( const CGTownInstance * t ) - > bool
2012-02-14 21:04:45 +03:00
{
return vstd : : contains ( t - > forbiddenBuildings , EBuilding : : GRAIL ) ;
2012-02-16 20:10:58 +03:00
} ) ,
2012-02-14 21:04:45 +03:00
towns . end ( ) ) ;
boost : : sort ( towns , isCloser ) ;
if ( towns . size ( ) )
{
return CGoal ( VISIT_TILE ) . sethero ( h ) . settile ( towns . front ( ) - > visitablePos ( ) ) ;
}
}
}
double ratio = 0 ;
int3 grailPos = cb - > getGrailPos ( ratio ) ;
if ( ratio > 0.99 )
{
return CGoal ( DIG_AT_TILE ) . settile ( grailPos ) ;
}
else if ( const CGObjectInstance * obj = ai - > getUnvisitedObj ( objWithID < Obj : : OBELISK > ) ) //there are unvisited Obelisks
{
return CGoal ( GET_OBJ ) . setobjid ( obj - > id ) ;
}
else
return CGoal ( EXPLORE ) ;
}
break ;
case EVictoryConditionType : : CAPTURECITY :
return CGoal ( GET_OBJ ) . setobjid ( vc . ID ) ;
case EVictoryConditionType : : GATHERRESOURCE :
return CGoal ( COLLECT_RES ) . setresID ( vc . ID ) . setvalue ( vc . count ) ;
2012-02-16 20:10:58 +03:00
//TODO mines? piles? marketplace?
2012-02-14 21:04:45 +03:00
//save?
break ;
case EVictoryConditionType : : GATHERTROOP :
break ;
case EVictoryConditionType : : TAKEDWELLINGS :
break ;
case EVictoryConditionType : : TAKEMINES :
break ;
case EVictoryConditionType : : TRANSPORTITEM :
break ;
case EVictoryConditionType : : WINSTANDARD :
return CGoal ( CONQUER ) ;
default :
assert ( 0 ) ;
}
}
break ;
case GET_OBJ :
{
const CGObjectInstance * obj = cb - > getObj ( objid ) ;
if ( ! obj )
return CGoal ( EXPLORE ) ;
int3 pos = cb - > getObj ( objid ) - > visitablePos ( ) ;
return CGoal ( VISIT_TILE ) . settile ( pos ) ;
}
break ;
case GET_ART_TYPE :
{
const CGObjectInstance * artInst = ai - > lookForArt ( aid ) ;
if ( ! artInst )
{
TSubgoal alternativeWay = CGoal : : lookForArtSmart ( aid ) ;
if ( alternativeWay . invalid ( ) )
return CGoal ( EXPLORE ) ;
else
return alternativeWay ;
}
else
return CGoal ( GET_OBJ ) . setobjid ( artInst - > id ) ;
}
break ;
case CLEAR_WAY_TO :
{
assert ( tile . x > = 0 ) ; //set tile
if ( ! cb - > isVisible ( tile ) )
{
tlog1 < < " Clear way should be used with visible tiles! \n " ;
return CGoal ( EXPLORE ) ;
}
const CGHeroInstance * h = hero ? hero : ai - > primaryHero ( ) ;
if ( ! h )
return CGoal ( RECRUIT_HERO ) ;
cb - > setSelection ( h ) ;
SectorMap sm ;
bool dropToFile = false ;
if ( dropToFile ) //for debug purposes
sm . write ( " test.txt " ) ;
int3 tileToHit = sm . firstTileToGet ( h , tile ) ;
//if(isSafeToVisit(h, tileToHit))
if ( isBlockedBorderGate ( tileToHit ) )
throw cannotFulfillGoalException ( " There's blocked border gate! " ) ;
if ( tileToHit = = tile )
{
2012-02-16 20:10:58 +03:00
tlog1 < < boost : : format ( " Very strange, tile to hit is %s and tile is also %s, while hero %s is at %s \n " )
2012-02-14 21:04:45 +03:00
% tileToHit % tile % h - > name % h - > visitablePos ( ) ;
throw cannotFulfillGoalException ( " Retreiving first tile to hit failed (probably) ! " ) ;
}
return CGoal ( VISIT_TILE ) . settile ( tileToHit ) . sethero ( h ) ;
//TODO czy istnieje lepsza droga?
}
throw cannotFulfillGoalException ( " Cannot reach given tile! " ) ;
//return CGoal(EXPLORE); // TODO improve
case EXPLORE :
{
auto hs = cb - > getHeroesInfo ( ) ;
2012-03-13 15:47:47 +03:00
int howManyHeroes = hs . size ( ) ;
2012-02-14 21:04:45 +03:00
erase ( hs , [ ] ( const CGHeroInstance * h )
{
2012-03-13 15:47:47 +03:00
return contains ( ai - > lockedHeroes , h ) ;
2012-02-14 21:04:45 +03:00
} ) ;
2012-03-13 15:47:47 +03:00
if ( hs . empty ( ) ) //all heroes are busy. buy new one
2012-02-14 21:04:45 +03:00
{
2012-03-13 15:47:47 +03:00
if ( howManyHeroes < 3 & & ai - > findTownWithTavern ( ) ) //we may want to recruit second hero. TODO: make it smart finally
return CGoal ( RECRUIT_HERO ) ;
else //find mobile hero with weakest army
{
hs = cb - > getHeroesInfo ( ) ;
erase_if ( hs , [ ] ( const CGHeroInstance * h )
{
return ! h - > movement ; //only hero with movement are of interest for us
} ) ;
if ( hs . empty ( ) )
{
if ( howManyHeroes < GameConstants : : MAX_HEROES_PER_PLAYER )
return CGoal ( RECRUIT_HERO ) ;
else
throw cannotFulfillGoalException ( " No heroes with remaining MPs for exploring! \n " ) ;
}
boost : : sort ( hs , compareHeroStrength ) ;
}
2012-02-14 21:04:45 +03:00
}
const CGHeroInstance * h = hs . front ( ) ;
CGoal ret ( VISIT_TILE ) ;
ret . sethero ( h ) ;
return ret . settile ( whereToExplore ( h ) ) ;
}
I_AM_ELEMENTAR ;
case RECRUIT_HERO :
{
const CGTownInstance * t = ai - > findTownWithTavern ( ) ;
if ( ! t )
return CGoal ( BUILD_STRUCTURE ) . setbid ( EBuilding : : TAVERN ) ;
if ( cb - > getResourceAmount ( Res : : GOLD ) < HERO_GOLD_COST )
return CGoal ( COLLECT_RES ) . setresID ( Res : : GOLD ) . setvalue ( HERO_GOLD_COST ) ;
I_AM_ELEMENTAR ;
}
break ;
case VISIT_TILE :
{
if ( ! cb - > isVisible ( tile ) )
return CGoal ( EXPLORE ) ;
2012-02-16 20:10:58 +03:00
2012-02-14 21:04:45 +03:00
if ( hero & & ! ai - > isAccessibleForHero ( tile , hero ) )
hero = NULL ;
if ( ! hero )
{
if ( cb - > getHeroesInfo ( ) . empty ( ) )
return CGoal ( RECRUIT_HERO ) ;
BOOST_FOREACH ( const CGHeroInstance * h , cb - > getHeroesInfo ( ) )
{
if ( ai - > isAccessibleForHero ( tile , h ) )
{
hero = h ;
break ;
}
}
}
if ( hero )
{
if ( isSafeToVisit ( hero , tile ) )
return CGoal ( * this ) . setisElementar ( true ) ;
else
2012-03-13 15:47:47 +03:00
return CGoal ( GATHER_ARMY ) . sethero ( hero ) ;
2012-02-14 21:04:45 +03:00
}
else //inaccessible for all heroes
return CGoal ( CLEAR_WAY_TO ) . settile ( tile ) ;
}
break ;
case DIG_AT_TILE :
{
auto objs = cb - > getTile ( tile ) - > visitableObjects ;
if ( objs . size ( ) & & objs . front ( ) - > ID = = GameConstants : : HEROI_TYPE & & objs . front ( ) - > tempOwner = = ai - > playerID ) //we have hero at dest
{
const CGHeroInstance * h = dynamic_cast < const CGHeroInstance * > ( objs . front ( ) ) ;
return CGoal ( * this ) . sethero ( h ) . setisElementar ( true ) ;
}
return CGoal ( VISIT_TILE ) . settile ( tile ) ;
}
break ;
case BUILD_STRUCTURE :
//TODO check res
2012-02-16 20:10:58 +03:00
//look for town
2012-02-14 21:04:45 +03:00
//prerequisites?
I_AM_ELEMENTAR ;
case COLLECT_RES :
{
2012-02-16 20:10:58 +03:00
2012-02-14 21:04:45 +03:00
std : : vector < const IMarket * > markets ;
std : : vector < const CGObjectInstance * > visObjs ;
ai - > retreiveVisitableObjs ( visObjs , true ) ;
BOOST_FOREACH ( const CGObjectInstance * obj , visObjs )
{
if ( const IMarket * m = IMarket : : castFrom ( obj , false ) )
{
if ( obj - > ID = = GameConstants : : TOWNI_TYPE & & obj - > tempOwner = = ai - > playerID & & m - > allowsTrade ( EMarketMode : : RESOURCE_RESOURCE ) )
markets . push_back ( m ) ;
2012-02-16 20:10:58 +03:00
else if ( obj - > ID = = Obj : : TRADING_POST ) //TODO a moze po prostu test na pozwalanie handlu?
2012-02-14 21:04:45 +03:00
markets . push_back ( m ) ;
}
}
boost : : sort ( markets , [ ] ( const IMarket * m1 , const IMarket * m2 ) - > bool
{
return m1 - > getMarketEfficiency ( ) < m2 - > getMarketEfficiency ( ) ;
} ) ;
2012-02-16 20:10:58 +03:00
markets . erase ( boost : : remove_if ( markets , [ ] ( const IMarket * market ) - > bool
2012-02-14 21:04:45 +03:00
{
return ! ( market - > o - > ID = = GameConstants : : TOWNI_TYPE & & market - > o - > tempOwner = = ai - > playerID )
& & ! ai - > isAccessible ( market - > o - > visitablePos ( ) ) ;
} ) , markets . end ( ) ) ;
if ( ! markets . size ( ) )
{
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
{
if ( cb - > canBuildStructure ( t , EBuilding : : MARKETPLACE ) = = EBuildingState : : ALLOWED )
return CGoal ( BUILD_STRUCTURE ) . settown ( t ) . setbid ( EBuilding : : MARKETPLACE ) ;
}
}
else
{
const IMarket * m = markets . back ( ) ;
//attempt trade at back (best prices)
int howManyCanWeBuy = 0 ;
for ( int i = 0 ; i < ACTUAL_RESOURCE_COUNT ; i + + )
{
if ( i = = resID ) continue ;
int toGive = - 1 , toReceive = - 1 ;
m - > getOffer ( i , resID , toGive , toReceive , EMarketMode : : RESOURCE_RESOURCE ) ;
assert ( toGive > 0 & & toReceive > 0 ) ;
howManyCanWeBuy + = toReceive * ( cb - > getResourceAmount ( i ) / toGive ) ;
}
if ( howManyCanWeBuy + cb - > getResourceAmount ( resID ) > = value )
{
if ( cb - > getTile ( m - > o - > visitablePos ( ) ) - > visitableObjects . back ( ) - > tempOwner ! = ai - > playerID )
return CGoal ( GET_OBJ ) . setobjid ( m - > o - > id ) ;
return setobjid ( m - > o - > id ) . setisElementar ( true ) ;
}
}
}
return CGoal ( INVALID ) ;
case CONQUER :
{
//TODO make use from many heroes
std : : vector < const CGHeroInstance * > heroes = cb - > getHeroesInfo ( ) ;
erase_if ( heroes , [ ] ( const CGHeroInstance * h )
{
2012-03-13 15:47:47 +03:00
return vstd : : contains ( ai - > lockedHeroes , h ) | | ! h - > movement ;
2012-02-14 21:04:45 +03:00
} ) ;
boost : : sort ( heroes , compareHeroStrength ) ;
if ( heroes . empty ( ) )
I_AM_ELEMENTAR ;
const CGHeroInstance * h = heroes . back ( ) ;
cb - > setSelection ( h ) ;
std : : vector < const CGObjectInstance * > objs ; //here we'll gather enemy towns and heroes
ai - > retreiveVisitableObjs ( objs ) ;
erase_if ( objs , [ & ] ( const CGObjectInstance * obj )
{
2012-02-16 20:10:58 +03:00
return ( obj - > ID ! = GameConstants : : TOWNI_TYPE & & obj - > ID ! = GameConstants : : HEROI_TYPE ) //not town/hero
2012-02-14 21:04:45 +03:00
| | cb - > getPlayerRelations ( ai - > playerID , obj - > tempOwner ) ! = 0 ; //not enemy
} ) ;
if ( objs . empty ( ) )
return CGoal ( EXPLORE ) ; //we need to find an enemy
erase_if ( objs , [ & ] ( const CGObjectInstance * obj )
{
return ! isSafeToVisit ( h , obj - > visitablePos ( ) ) ;
} ) ;
if ( objs . empty ( ) )
I_AM_ELEMENTAR ;
boost : : sort ( objs , isCloser ) ;
BOOST_FOREACH ( const CGObjectInstance * obj , objs )
{
if ( ai - > isAccessibleForHero ( obj - > visitablePos ( ) , h ) )
return CGoal ( VISIT_TILE ) . sethero ( h ) . settile ( obj - > visitablePos ( ) ) ;
}
return CGoal ( EXPLORE ) ; //enemy is inaccessible
}
break ;
case BUILD :
I_AM_ELEMENTAR ;
case INVALID :
I_AM_ELEMENTAR ;
2012-03-13 15:47:47 +03:00
case GATHER_ARMY :
{
const CGHeroInstance * h = hero ;
auto compareReinforcements = [ h ] ( const CGTownInstance * lhs , const CGTownInstance * rhs ) - > bool
{
return howManyReinforcementsCanGet ( h , lhs ) < howManyReinforcementsCanGet ( h , rhs ) ;
} ;
std : : vector < const CGTownInstance * > townsReachable ;
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
{
if ( ! t - > visitingHero & & howManyReinforcementsCanGet ( h , t ) )
{
if ( isReachable ( t ) )
townsReachable . push_back ( t ) ;
}
}
if ( townsReachable . size ( ) ) //try towns first
{
boost : : sort ( townsReachable , compareReinforcements ) ;
return CGoal ( VISIT_TILE ) . sethero ( hero ) . settile ( townsReachable . back ( ) - > visitablePos ( ) ) ;
}
else
{
std : : vector < const CGObjectInstance * > objs ; //here we'll gather all dwellings
ai - > retreiveVisitableObjs ( objs ) ;
erase_if ( objs , [ & ] ( const CGObjectInstance * obj )
{
return ( obj - > ID ! = Obj : : CREATURE_GENERATOR1 ) ; //not town/ dwelling
} ) ;
if ( objs . empty ( ) ) //no possible objects, we did eveyrthing already
return CGoal ( EXPLORE ) . sethero ( hero ) ;
//TODO: check if we can recruit any creatures there, evaluate army
boost : : sort ( objs , isCloser ) ;
BOOST_FOREACH ( const CGObjectInstance * obj , objs )
{ //find safe dwelling
if ( isSafeToVisit ( hero , obj - > visitablePos ( ) ) ) //TODO: make use of multiple heroes
return CGoal ( VISIT_TILE ) . sethero ( hero ) . settile ( obj - > visitablePos ( ) ) ;
}
}
return CGoal ( EXPLORE ) ; //find dwelling
}
break ;
2012-02-14 21:04:45 +03:00
default :
assert ( 0 ) ;
}
return CGoal ( EXPLORE ) ;
}
TSubgoal CGoal : : goVisitOrLookFor ( const CGObjectInstance * obj )
{
if ( obj )
return CGoal ( GET_OBJ ) . setobjid ( obj - > id ) ;
else
return CGoal ( EXPLORE ) ;
}
TSubgoal CGoal : : lookForArtSmart ( int aid )
{
return CGoal ( INVALID ) ;
}
bool CGoal : : invalid ( ) const
{
return goalType = = INVALID ;
}
bool CGoal : : isBlockedBorderGate ( int3 tileToHit )
{
2012-02-16 20:10:58 +03:00
return cb - > getTile ( tileToHit ) - > topVisitableID ( ) = = Obj : : BORDER_GATE
2012-02-14 21:04:45 +03:00
& & cb - > getPathInfo ( tileToHit ) - > accessible ! = CGPathNode : : ACCESSIBLE ;
}
SectorMap : : SectorMap ( )
{
// int3 sizes = cb->getMapSize();
// sector.resize(sizes.x);
// BOOST_FOREACH(auto &i, sector)
// i.resize(sizes.y);
2012-02-16 20:10:58 +03:00
//
2012-02-14 21:04:45 +03:00
// BOOST_FOREACH(auto &i, sector)
// BOOST_FOREACH(auto &j, i)
// j.resize(sizes.z, 0);
update ( ) ;
}
bool markIfBlocked ( ui8 & sec , crint3 pos , const TerrainTile * t )
{
if ( t - > blocked & & ! t - > visitable )
{
sec = NOT_AVAILABLE ;
return true ;
}
return false ;
}
bool markIfBlocked ( ui8 & sec , crint3 pos )
{
return markIfBlocked ( sec , pos , cb - > getTile ( pos ) ) ;
}
void SectorMap : : update ( )
{
clear ( ) ;
int curSector = 3 ; //0 is invisible, 1 is not explored
foreach_tile_pos ( [ & ] ( crint3 pos )
{
if ( retreiveTile ( pos ) = = NOT_CHECKED )
{
if ( ! markIfBlocked ( retreiveTile ( pos ) , pos ) )
exploreNewSector ( pos , curSector + + ) ;
}
} ) ;
valid = true ;
}
void SectorMap : : clear ( )
{
sector = cb - > getVisibilityMap ( ) ;
valid = false ;
}
bool canBeEmbarkmentPoint ( const TerrainTile * t )
{
//tile must be free of with unoccupied boat
2012-02-16 20:10:58 +03:00
return ! t - > blocked
| | ( t - > visitableObjects . size ( ) = = 1 & & t - > topVisitableID ( ) = = Obj : : BOAT ) ;
2012-02-14 21:04:45 +03:00
}
void SectorMap : : exploreNewSector ( crint3 pos , int num )
{
Sector & s = infoOnSectors [ num ] ;
s . id = num ;
s . water = cb - > getTile ( pos ) - > isWater ( ) ;
std : : queue < int3 > toVisit ;
toVisit . push ( pos ) ;
while ( toVisit . size ( ) )
{
int3 curPos = toVisit . front ( ) ;
toVisit . pop ( ) ;
ui8 & sec = retreiveTile ( curPos ) ;
if ( sec = = NOT_CHECKED )
{
const TerrainTile * t = cb - > getTile ( curPos ) ;
if ( ! markIfBlocked ( sec , curPos , t ) )
{
if ( t - > isWater ( ) = = s . water ) //sector is only-water or only-land
{
sec = num ;
s . tiles . push_back ( curPos ) ;
foreach_neighbour ( curPos , [ & ] ( crint3 neighPos )
{
if ( retreiveTile ( neighPos ) = = NOT_CHECKED )
{
toVisit . push ( neighPos ) ;
//parent[neighPos] = curPos;
}
const TerrainTile * nt = cb - > getTile ( neighPos , false ) ;
if ( nt & & nt - > isWater ( ) ! = s . water & & canBeEmbarkmentPoint ( nt ) )
{
s . embarkmentPoints . push_back ( neighPos ) ;
}
} ) ;
if ( t - > visitable & & vstd : : contains ( ai - > knownSubterraneanGates , t - > visitableObjects . front ( ) ) )
toVisit . push ( ai - > knownSubterraneanGates [ t - > visitableObjects . front ( ) ] - > pos ) ;
}
}
}
}
removeDuplicates ( s . embarkmentPoints ) ;
}
void SectorMap : : write ( crstring fname )
{
std : : ofstream out ( fname ) ;
for ( int k = 0 ; k < cb - > getMapSize ( ) . z ; k + + )
{
for ( int j = 0 ; j < cb - > getMapSize ( ) . y ; j + + )
{
for ( int i = 0 ; i < cb - > getMapSize ( ) . x ; i + + )
{
out < < ( int ) sector [ i ] [ j ] [ k ] < < ' \t ' ;
}
out < < std : : endl ;
}
out < < std : : endl ;
}
}
2012-03-05 22:11:28 +03:00
bool isWeeklyRevisitable ( const CGObjectInstance * obj )
{ //TODO: allow polling of remaining creatures in dwelling
2012-03-14 16:02:38 +03:00
if ( dynamic_cast < const CGVisitableOPW * > ( obj ) | | //ensures future compatibility, unlike IDs
dynamic_cast < const CGDwelling * > ( obj ) | |
dynamic_cast < const CBank * > ( obj ) ) //banks tend to respawn often in mods
2012-03-05 22:11:28 +03:00
return true ;
2012-03-06 21:49:23 +03:00
switch ( obj - > ID )
{
case Obj : : STABLES : //any other potential visitable objects?
return true ;
}
2012-03-05 22:11:28 +03:00
return false ;
}
2012-03-14 16:02:38 +03:00
bool shouldVisit ( const CGHeroInstance * h , const CGObjectInstance * obj )
{
switch ( obj - > ID )
{
case Obj : : CREATURE_GENERATOR1 :
{
bool canRecruitCreatures = false ;
const CGDwelling * d = dynamic_cast < const CGDwelling * > ( obj ) ;
BOOST_FOREACH ( auto level , d - > creatures )
{
BOOST_FOREACH ( auto c , level . second )
{
if ( h - > getSlotFor ( c ) ! = - 1 )
canRecruitCreatures = true ;
}
}
return canRecruitCreatures ;
}
case Obj : : MONOLITH1 :
case Obj : : MONOLITH2 :
case Obj : : MONOLITH3 :
case Obj : : WHIRLPOOL :
//TODO: mehcanism for handling monoliths
return false ;
2012-03-29 21:26:06 +03:00
case Obj : : SCHOOL_OF_MAGIC :
case Obj : : SCHOOL_OF_WAR :
{
TResources myRes = ai - > myCb - > getResourceAmount ( ) ;
if ( myRes [ Res : : GOLD ] - GOLD_RESERVE < 1000 )
return false ;
}
2012-04-04 11:03:52 +03:00
break ;
2012-03-29 21:26:06 +03:00
case Obj : : LIBRARY_OF_ENLIGHTENMENT :
2012-03-29 21:29:47 +03:00
if ( h - > level < 12 )
return false ;
2012-04-04 11:03:52 +03:00
break ;
2012-03-14 16:02:38 +03:00
}
2012-03-29 21:26:06 +03:00
if ( obj - > wasVisited ( h ) )
return false ;
2012-03-14 16:02:38 +03:00
return true ;
}
2012-02-14 21:04:45 +03:00
int3 SectorMap : : firstTileToGet ( const CGHeroInstance * h , crint3 dst )
{
2012-02-16 20:10:58 +03:00
int sourceSector = retreiveTile ( h - > visitablePos ( ) ) ,
2012-02-14 21:04:45 +03:00
destinationSector = retreiveTile ( dst ) ;
if ( sourceSector ! = destinationSector )
{
const Sector * src = & infoOnSectors [ sourceSector ] ,
* dst = & infoOnSectors [ destinationSector ] ;
std : : map < const Sector * , const Sector * > preds ;
std : : queue < const Sector * > sq ;
sq . push ( src ) ;
while ( ! sq . empty ( ) )
{
const Sector * s = sq . front ( ) ;
sq . pop ( ) ;
BOOST_FOREACH ( int3 ep , s - > embarkmentPoints )
{
Sector * neigh = & infoOnSectors [ retreiveTile ( ep ) ] ;
//preds[s].push_back(neigh);
if ( ! preds [ neigh ] )
{
preds [ neigh ] = s ;
sq . push ( neigh ) ;
}
}
//TODO consider other types of connections between sectors?
}
if ( ! preds [ dst ] )
{
write ( " test.txt " ) ;
throw cannotFulfillGoalException ( str ( format ( " Cannot found connection between sectors %d and %d " ) % src - > id % dst - > id ) ) ;
}
std : : vector < const Sector * > toTraverse ;
toTraverse . push_back ( dst ) ;
while ( toTraverse . back ( ) ! = src )
{
toTraverse . push_back ( preds [ toTraverse . back ( ) ] ) ;
}
if ( preds [ dst ] )
{
const Sector * sectorToReach = toTraverse . at ( toTraverse . size ( ) - 2 ) ;
if ( ! src - > water & & sectorToReach - > water ) //embark
{
//embark on ship -> look for an EP with a boat
auto firstEP = boost : : find_if ( src - > embarkmentPoints , [ = ] ( crint3 pos ) - > bool
{
const TerrainTile * t = cb - > getTile ( pos ) ;
return t & & t - > visitableObjects . size ( ) = = 1 & & t - > topVisitableID ( ) = = Obj : : BOAT
& & retreiveTile ( pos ) = = sectorToReach - > id ;
} ) ;
if ( firstEP ! = src - > embarkmentPoints . end ( ) )
{
return * firstEP ;
}
else
{
//we need to find a shipyard with an access to the desired sector's EP
//TODO what about Summon Boat spell?
std : : vector < const IShipyard * > shipyards ;
BOOST_FOREACH ( const CGTownInstance * t , cb - > getTownsInfo ( ) )
{
if ( t - > hasBuilt ( EBuilding : : SHIPYARD ) )
shipyards . push_back ( t ) ;
}
std : : vector < const CGObjectInstance * > visObjs ;
ai - > retreiveVisitableObjs ( visObjs , true ) ;
BOOST_FOREACH ( const CGObjectInstance * obj , visObjs )
{
if ( obj - > ID ! = GameConstants : : TOWNI_TYPE ) //towns were handled in the previous loop
if ( const IShipyard * shipyard = IShipyard : : castFrom ( obj ) )
shipyards . push_back ( shipyard ) ;
}
2012-02-16 20:10:58 +03:00
shipyards . erase ( boost : : remove_if ( shipyards , [ = ] ( const IShipyard * shipyard ) - > bool
2012-02-14 21:04:45 +03:00
{
return shipyard - > state ( ) ! = 0 | | retreiveTile ( shipyard - > bestLocation ( ) ) ! = sectorToReach - > id ;
} ) , shipyards . end ( ) ) ;
if ( ! shipyards . size ( ) )
{
//TODO consider possibility of building shipyard in a town
throw cannotFulfillGoalException ( " There is no known shipyard! " ) ;
}
//we have only shipyards that possibly can build ships onto the appropriate EP
auto ownedGoodShipyard = boost : : find_if ( shipyards , [ ] ( const IShipyard * s ) - > bool
{
return s - > o - > tempOwner = = ai - > playerID ;
} ) ;
if ( ownedGoodShipyard ! = shipyards . end ( ) )
{
const IShipyard * s = * ownedGoodShipyard ;
TResources shipCost ;
s - > getBoatCost ( shipCost ) ;
if ( cb - > getResourceAmount ( ) . canAfford ( shipCost ) )
{
int3 ret = s - > bestLocation ( ) ;
cb - > buildBoat ( s ) ;
return ret ;
}
else
{
//TODO gather res
throw cannotFulfillGoalException ( " Not enough resources to build a boat " ) ;
}
}
else
{
//TODO pick best shipyard to take over
return shipyards . front ( ) - > o - > pos ;
}
}
}
else if ( src - > water & & ! sectorToReach - > water )
{
//TODO
//disembark
}
else
{
//TODO
//transition between two land/water sectors. Monolith? Whirlpool? ...
throw cannotFulfillGoalException ( " Land-land and water-water inter-sector transitions are not implemented! " ) ;
}
}
else
{
throw cannotFulfillGoalException ( " Inter-sector route detection failed: not connected sectors? " ) ;
}
}
else
{
makeParentBFS ( h - > visitablePos ( ) ) ;
int3 curtile = dst ;
while ( curtile ! = h - > visitablePos ( ) )
{
if ( cb - > getPathInfo ( curtile ) - > reachable ( ) )
{
return curtile ;
}
else
{
auto i = parent . find ( curtile ) ;
if ( i ! = parent . end ( ) )
{
assert ( curtile ! = i - > second ) ;
curtile = i - > second ;
}
else
throw cannotFulfillGoalException ( " Unreachable tile in sector? Should not happen! " ) ;
}
}
}
throw cannotFulfillGoalException ( " Impossible happened. " ) ;
}
void SectorMap : : makeParentBFS ( crint3 source )
{
parent . clear ( ) ;
int mySector = retreiveTile ( source ) ;
std : : queue < int3 > toVisit ;
toVisit . push ( source ) ;
while ( toVisit . size ( ) )
{
int3 curPos = toVisit . front ( ) ;
toVisit . pop ( ) ;
ui8 & sec = retreiveTile ( curPos ) ;
assert ( sec = = mySector ) ; //consider only tiles from the same sector
2012-02-16 20:10:58 +03:00
//const TerrainTile *t = cb->getTile(curPos);
2012-02-14 21:04:45 +03:00
foreach_neighbour ( curPos , [ & ] ( crint3 neighPos )
{
if ( retreiveTile ( neighPos ) = = mySector & & ! vstd : : contains ( parent , neighPos ) )
{
toVisit . push ( neighPos ) ;
parent [ neighPos ] = curPos ;
}
} ) ;
}
}
unsigned char & SectorMap : : retreiveTile ( crint3 pos )
{
return retreiveTileN ( sector , pos ) ;
2012-04-08 13:34:23 +03:00
}