2013-10-18 20:17:25 +00:00
/*
* Goals . 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
*
*/
2017-07-13 11:26:03 +03:00
# include "StdInc.h"
# include "Goals.h"
# include "VCAI.h"
2018-08-10 18:27:57 +02:00
# include "FuzzyHelper.h"
2018-07-26 12:06:55 +02:00
# include "ResourceManager.h"
2018-07-29 18:27:21 +02:00
# include "BuildingManager.h"
2017-07-13 11:26:03 +03:00
# include "../../lib/mapping/CMap.h" //for victory conditions
# include "../../lib/CPathfinder.h"
2018-07-29 22:07:40 +03:00
# include "../../lib/StringConstants.h"
2018-07-26 12:06:55 +02:00
# include "AIhelper.h"
2013-10-18 20:17:25 +00:00
extern boost : : thread_specific_ptr < CCallback > cb ;
extern boost : : thread_specific_ptr < VCAI > ai ;
2018-07-26 12:06:55 +02:00
extern FuzzyHelper * fh ;
2013-10-18 20:17:25 +00:00
2013-10-19 05:52:30 +00:00
using namespace Goals ;
2013-10-18 20:17:25 +00:00
2013-11-23 18:16:25 +00:00
TSubgoal Goals : : sptr ( const AbstractGoal & tmp )
{
2018-07-26 12:06:55 +02:00
TSubgoal ptr ;
2013-11-23 18:16:25 +00:00
ptr . reset ( tmp . clone ( ) ) ;
return ptr ;
}
2013-11-15 17:30:16 +00:00
std : : string Goals : : AbstractGoal : : name ( ) const //TODO: virtualize
2013-10-18 20:17:25 +00:00
{
2013-12-23 20:46:01 +00:00
std : : string desc ;
2018-04-07 15:44:14 +07:00
switch ( goalType )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
case INVALID :
return " INVALID " ;
case WIN :
return " WIN " ;
case DO_NOT_LOSE :
return " DO NOT LOOSE " ;
case CONQUER :
return " CONQUER " ;
case BUILD :
return " BUILD " ;
case EXPLORE :
desc = " EXPLORE " ;
break ;
case GATHER_ARMY :
desc = " GATHER ARMY " ;
break ;
2018-07-26 12:06:55 +02:00
case BUY_ARMY :
return " BUY ARMY " ;
break ;
2018-04-07 15:44:14 +07:00
case BOOST_HERO :
desc = " BOOST_HERO (unsupported) " ;
break ;
case RECRUIT_HERO :
return " RECRUIT HERO " ;
case BUILD_STRUCTURE :
return " BUILD STRUCTURE " ;
case COLLECT_RES :
2018-07-26 12:06:55 +02:00
desc = " COLLECT RESOURCE " + GameConstants : : RESOURCE_NAMES [ resID ] + " ( " + boost : : lexical_cast < std : : string > ( value ) + " ) " ;
2018-04-07 15:44:14 +07:00
break ;
2018-08-04 10:20:40 +02:00
case TRADE :
{
auto obj = cb - > getObjInstance ( ObjectInstanceID ( objid ) ) ;
if ( obj )
desc = ( boost : : format ( " TRADE %d of %s at %s " ) % value % GameConstants : : RESOURCE_NAMES [ resID ] % obj - > getObjectName ( ) ) . str ( ) ;
}
break ;
2018-04-07 15:44:14 +07:00
case GATHER_TROOPS :
desc = " GATHER TROOPS " ;
break ;
2018-08-10 16:48:42 +02:00
case VISIT_OBJ :
2018-04-07 15:44:14 +07:00
{
auto obj = cb - > getObjInstance ( ObjectInstanceID ( objid ) ) ;
if ( obj )
desc = " GET OBJ " + obj - > getObjectName ( ) ;
}
break ;
case FIND_OBJ :
desc = " FIND OBJ " + boost : : lexical_cast < std : : string > ( objid ) ;
break ;
case VISIT_HERO :
{
auto obj = cb - > getObjInstance ( ObjectInstanceID ( objid ) ) ;
if ( obj )
desc = " VISIT HERO " + obj - > getObjectName ( ) ;
}
break ;
case GET_ART_TYPE :
desc = " GET ARTIFACT OF TYPE " + VLC - > arth - > artifacts [ aid ] - > Name ( ) ;
break ;
case ISSUE_COMMAND :
return " ISSUE COMMAND (unsupported) " ;
case VISIT_TILE :
desc = " VISIT TILE " + tile . toString ( ) ;
break ;
case CLEAR_WAY_TO :
desc = " CLEAR WAY TO " + tile . toString ( ) ;
break ;
case DIG_AT_TILE :
desc = " DIG AT TILE " + tile . toString ( ) ;
break ;
default :
return boost : : lexical_cast < std : : string > ( goalType ) ;
2013-10-18 20:17:25 +00:00
}
2018-04-07 15:44:14 +07:00
if ( hero . get ( true ) ) //FIXME: used to crash when we lost hero and failed goal
2013-12-23 20:46:01 +00:00
desc + = " ( " + hero - > name + " ) " ;
return desc ;
2013-10-18 20:17:25 +00:00
}
2018-10-09 22:31:44 +03:00
bool Goals : : BuildBoat : : operator = = ( const BuildBoat & other ) const
2014-02-19 16:23:47 +00:00
{
2018-10-09 22:31:44 +03:00
return shipyard - > o - > id = = other . shipyard - > o - > id ;
}
2014-02-19 16:23:47 +00:00
2018-10-09 22:31:44 +03:00
bool Goals : : Explore : : operator = = ( const Explore & other ) const
{
return other . hero . h = = hero . h ;
}
2018-04-07 15:44:14 +07:00
2018-10-09 22:31:44 +03:00
bool Goals : : Conquer : : operator = = ( const Conquer & other ) const
{
return other . hero . h = = hero . h ;
}
2018-04-07 15:44:14 +07:00
2018-10-09 22:31:44 +03:00
bool Goals : : GatherArmy : : operator = = ( const GatherArmy & other ) const
{
return other . hero . h = = hero . h | | town = = other . town ;
}
2018-07-26 12:06:55 +02:00
2018-10-09 22:31:44 +03:00
bool Goals : : BuyArmy : : operator = = ( const BuyArmy & other ) const
{
return town = = other . town ;
}
2018-04-07 15:44:14 +07:00
2018-10-09 22:31:44 +03:00
bool Goals : : BoostHero : : operator = = ( const BoostHero & other ) const
{
return other . hero . h = = hero . h ;
}
2018-04-07 15:44:14 +07:00
2018-10-09 22:31:44 +03:00
bool Goals : : BuildThis : : operator = = ( const BuildThis & other ) const
{
return town = = other . town & & bid = = other . bid ;
}
2018-07-26 12:06:55 +02:00
2018-10-09 22:31:44 +03:00
bool Goals : : CollectRes : : operator = = ( const CollectRes & other ) const
{
return resID = = other . resID ;
}
2018-09-11 13:09:14 +03:00
2018-10-09 22:31:44 +03:00
bool Goals : : Trade : : operator = = ( const Trade & other ) const
{
return resID = = other . resID ;
}
bool Goals : : GatherTroops : : operator = = ( const GatherTroops & other ) const
{
return objid = = other . objid ;
}
bool Goals : : VisitObj : : operator = = ( const VisitObj & other ) const
{
return other . hero . h = = hero . h & & other . objid = = objid ;
}
bool Goals : : FindObj : : operator = = ( const FindObj & other ) const
{
return other . hero . h = = hero . h & & other . objid = = objid ;
}
bool Goals : : VisitHero : : operator = = ( const VisitHero & other ) const
{
return other . hero . h = = hero . h & & other . objid = = objid ;
}
bool Goals : : GetArtOfType : : operator = = ( const GetArtOfType & other ) const
{
return other . hero . h = = hero . h & & other . objid = = objid ;
}
bool Goals : : VisitTile : : operator = = ( const VisitTile & other ) const
{
return other . hero . h = = hero . h & & other . tile = = tile ;
}
bool Goals : : ClearWayTo : : operator = = ( const ClearWayTo & other ) const
{
return other . hero . h = = hero . h & & other . tile = = tile ;
}
bool Goals : : DigAtTile : : operator = = ( const DigAtTile & other ) const
{
return other . hero . h = = hero . h & & other . tile = = tile ;
}
bool Goals : : AbstractGoal : : operator = = ( const AbstractGoal & g ) const
{
return false ;
2014-02-19 16:23:47 +00:00
}
2018-08-05 09:21:41 +02:00
bool Goals : : AbstractGoal : : operator < ( AbstractGoal & g ) //for std::unique
{
//TODO: make sure it gets goals consistent with == operator
if ( goalType < g . goalType )
return true ;
if ( goalType > g . goalType )
return false ;
if ( hero < g . hero )
return true ;
if ( hero > g . hero )
return false ;
if ( tile < g . tile )
return true ;
if ( g . tile < tile )
return false ;
if ( objid < g . objid )
return true ;
if ( objid > g . objid )
return false ;
if ( town < g . town )
return true ;
if ( town > g . town )
return false ;
if ( value < g . value )
return true ;
if ( value > g . value )
return false ;
if ( priority < g . priority )
return true ;
if ( priority > g . priority )
return false ;
if ( resID < g . resID )
return true ;
if ( resID > g . resID )
return false ;
if ( bid < g . bid )
return true ;
if ( bid > g . bid )
return false ;
if ( aid < g . aid )
return true ;
if ( aid > g . aid )
return false ;
return false ;
}
2013-11-30 07:52:08 +00:00
//TODO: find out why the following are not generated automatically on MVS?
2016-01-19 20:25:15 +03:00
namespace Goals
{
2018-07-26 12:06:55 +02:00
template < >
void CGoal < Win > : : accept ( VCAI * ai )
{
ai - > tryRealize ( static_cast < Win & > ( * this ) ) ;
}
2013-11-30 07:52:08 +00:00
2018-07-26 12:06:55 +02:00
template < >
void CGoal < Build > : : accept ( VCAI * ai )
{
ai - > tryRealize ( static_cast < Build & > ( * this ) ) ;
}
template < >
float CGoal < Win > : : accept ( FuzzyHelper * f )
{
return f - > evaluate ( static_cast < Win & > ( * this ) ) ;
}
2013-12-19 20:21:21 +00:00
2018-07-26 12:06:55 +02:00
template < >
float CGoal < Build > : : accept ( FuzzyHelper * f )
{
return f - > evaluate ( static_cast < Build & > ( * this ) ) ;
}
bool TSubgoal : : operator = = ( const TSubgoal & rhs ) const
{
return * get ( ) = = * rhs . get ( ) ; //comparison for Goals is overloaded, so they don't need to be identical to match
}
2018-08-05 09:21:41 +02:00
bool TSubgoal : : operator < ( const TSubgoal & rhs ) const
2018-07-26 12:06:55 +02:00
{
2018-08-05 09:21:41 +02:00
return get ( ) < rhs . get ( ) ; //compae by value
}
2018-07-26 12:06:55 +02:00
bool BuyArmy : : fulfillsMe ( TSubgoal goal )
{
//if (hero && hero != goal->hero)
// return false;
return town = = goal - > town & & goal - > value > = value ; //can always buy more army
}
TSubgoal BuyArmy : : whatToDoToAchieve ( )
{
//TODO: calculate the actual cost of units instead
TResources price ;
price [ Res : : GOLD ] = value * 0.4f ; //some approximate value
2018-09-02 18:04:38 +03:00
return ai - > ah - > whatToDo ( price , iAmElementar ( ) ) ; //buy right now or gather resources
2018-07-26 12:06:55 +02:00
}
std : : string BuyArmy : : completeMessage ( ) const
{
2018-07-27 09:22:07 +02:00
return boost : : format ( " Bought army of value %d in town of %s " ) % value , town - > name ;
2018-07-26 12:06:55 +02:00
}
2013-11-30 07:52:08 +00:00
}
2018-08-04 10:20:40 +02:00
TSubgoal Trade : : whatToDoToAchieve ( )
{
return iAmElementar ( ) ;
}
2013-11-23 18:16:25 +00:00
//TSubgoal AbstractGoal::whatToDoToAchieve()
//{
2016-08-13 17:44:37 +03:00
// logAi->debug("Decomposing goal of type %s",name());
2016-03-12 04:41:27 +03:00
// return sptr (Goals::Explore());
2013-11-23 18:16:25 +00:00
//}
2013-10-18 20:17:25 +00:00
TSubgoal Win : : whatToDoToAchieve ( )
{
2013-12-29 11:27:38 +00:00
auto toBool = [ = ] ( const EventCondition & )
{
// TODO: proper implementation
// Right now even already fulfilled goals will be included into generated list
// Proper check should test if event condition is already fulfilled
// Easiest way to do this is to call CGameState::checkForVictory but this function should not be
// used on client side or in AI code
return false ;
} ;
std : : vector < EventCondition > goals ;
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
for ( const TriggeredEvent & event : cb - > getMapHeader ( ) - > triggeredEvents )
2013-10-18 20:17:25 +00:00
{
2013-12-29 11:27:38 +00:00
//TODO: try to eliminate human player(s) using loss conditions that have isHuman element
2018-04-07 15:44:14 +07:00
if ( event . effect . type = = EventEffect : : VICTORY )
2013-12-29 11:27:38 +00:00
{
boost : : range : : copy ( event . trigger . getFulfillmentCandidates ( toBool ) , std : : back_inserter ( goals ) ) ;
}
2013-10-18 20:17:25 +00:00
}
2013-12-29 11:27:38 +00:00
//TODO: instead of returning first encountered goal AI should generate list of possible subgoals
2018-04-07 15:44:14 +07:00
for ( const EventCondition & goal : goals )
2013-10-18 20:17:25 +00:00
{
2013-12-29 11:27:38 +00:00
switch ( goal . condition )
2013-10-18 20:17:25 +00:00
{
2013-12-29 11:27:38 +00:00
case EventCondition : : HAVE_ARTIFACT :
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : GetArtOfType ( goal . objectType ) ) ;
2013-12-29 11:27:38 +00:00
case EventCondition : : DESTROY :
2018-04-07 15:44:14 +07:00
{
if ( goal . object )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
auto obj = cb - > getObj ( goal . object - > id ) ;
if ( obj )
if ( obj - > getOwner ( ) = = ai - > playerID ) //we can't capture our own object
return sptr ( Goals : : Conquer ( ) ) ;
2015-04-13 18:45:43 +02:00
2018-08-10 16:42:55 +02:00
return sptr ( Goals : : VisitObj ( goal . object - > id . getNum ( ) ) ) ;
2013-12-29 11:27:38 +00:00
}
2018-04-07 15:44:14 +07:00
else
2013-12-29 11:27:38 +00:00
{
2018-04-07 15:44:14 +07:00
// TODO: destroy all objects of type goal.objectType
// This situation represents "kill all creatures" condition from H3
break ;
}
}
case EventCondition : : HAVE_BUILDING :
{
2013-12-29 11:27:38 +00:00
// TODO build other buildings apart from Grail
// goal.objectType = buidingID to build
// goal.object = optional, town in which building should be built
// Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
2018-04-07 15:44:14 +07:00
if ( goal . objectType = = BuildingID : : GRAIL )
{
if ( auto h = ai - > getHeroWithGrail ( ) )
2013-12-29 11:27:38 +00:00
{
2018-04-07 15:44:14 +07:00
//hero is in a town that can host Grail
if ( h - > visitedTown & & ! vstd : : contains ( h - > visitedTown - > forbiddenBuildings , BuildingID : : GRAIL ) )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
const CGTownInstance * t = h - > visitedTown ;
2018-07-26 12:06:55 +02:00
return sptr ( Goals : : BuildThis ( BuildingID : : GRAIL , t ) . setpriority ( 10 ) ) ;
2018-04-07 15:44:14 +07:00
}
else
{
auto towns = cb - > getTownsInfo ( ) ;
towns . erase ( boost : : remove_if ( towns ,
[ ] ( const CGTownInstance * t ) - > bool
2013-12-29 11:27:38 +00:00
{
2018-04-07 15:44:14 +07:00
return vstd : : contains ( t - > forbiddenBuildings , BuildingID : : GRAIL ) ;
} ) ,
towns . end ( ) ) ;
boost : : sort ( towns , CDistanceSorter ( h . get ( ) ) ) ;
if ( towns . size ( ) )
{
return sptr ( Goals : : VisitTile ( towns . front ( ) - > visitablePos ( ) ) . sethero ( h ) ) ;
2013-12-29 11:27:38 +00:00
}
2013-10-18 20:17:25 +00:00
}
}
2018-04-07 15:44:14 +07:00
double ratio = 0 ;
// maybe make this check a bit more complex? For example:
// 0.75 -> dig randomly within 3 tiles radius
// 0.85 -> radius now 2 tiles
// 0.95 -> 1 tile radius, position is fully known
// AFAIK H3 AI does something like this
int3 grailPos = cb - > getGrailPos ( & ratio ) ;
if ( ratio > 0.99 )
{
return sptr ( Goals : : DigAtTile ( grailPos ) ) ;
} //TODO: use FIND_OBJ
else if ( const CGObjectInstance * obj = ai - > getUnvisitedObj ( objWithID < Obj : : OBELISK > ) ) //there are unvisited Obelisks
2018-08-10 16:42:55 +02:00
return sptr ( Goals : : VisitObj ( obj - > id . getNum ( ) ) ) ;
2018-04-07 15:44:14 +07:00
else
return sptr ( Goals : : Explore ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2018-04-07 15:44:14 +07:00
break ;
}
2013-12-29 11:27:38 +00:00
case EventCondition : : CONTROL :
2018-04-07 15:44:14 +07:00
{
if ( goal . object )
2013-10-18 20:17:25 +00:00
{
2018-11-06 21:45:29 +02:00
auto objRelations = cb - > getPlayerRelations ( ai - > playerID , goal . object - > tempOwner ) ;
if ( objRelations = = PlayerRelations : : ENEMIES )
{
return sptr ( Goals : : VisitObj ( goal . object - > id . getNum ( ) ) ) ;
}
else
{
// TODO: Defance
break ;
}
2018-04-07 15:44:14 +07:00
}
else
{
//TODO: control all objects of type "goal.objectType"
// Represents H3 condition "Flag all mines"
break ;
2013-12-29 11:27:38 +00:00
}
2018-04-07 15:44:14 +07:00
}
2013-12-29 11:27:38 +00:00
case EventCondition : : HAVE_RESOURCES :
//TODO mines? piles? marketplace?
//save?
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : CollectRes ( static_cast < Res : : ERes > ( goal . objectType ) , goal . value ) ) ;
2013-12-29 11:27:38 +00:00
case EventCondition : : HAVE_CREATURES :
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : GatherTroops ( goal . objectType , goal . value ) ) ;
2013-12-29 11:27:38 +00:00
case EventCondition : : TRANSPORT :
2018-04-07 15:44:14 +07:00
{
//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
// Represents "transport artifact" condition:
// goal.objectType = type of artifact
// goal.object = destination-town where artifact should be transported
break ;
}
2013-12-29 11:27:38 +00:00
case EventCondition : : STANDARD_WIN :
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Conquer ( ) ) ;
2013-12-29 11:27:38 +00:00
// Conditions that likely don't need any implementation
case EventCondition : : DAYS_PASSED :
break ; // goal.value = number of days for condition to trigger
case EventCondition : : DAYS_WITHOUT_TOWN :
break ; // goal.value = number of days to trigger this
case EventCondition : : IS_HUMAN :
break ; // Should be only used in calculation of candidates (see toBool lambda)
2014-02-09 12:10:02 +00:00
case EventCondition : : CONST_VALUE :
break ;
2016-11-13 13:38:42 +03:00
case EventCondition : : HAVE_0 :
case EventCondition : : HAVE_BUILDING_0 :
case EventCondition : : DESTROY_0 :
//TODO: support new condition format
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Conquer ( ) ) ;
2013-12-29 11:27:38 +00:00
default :
assert ( 0 ) ;
2013-10-18 20:17:25 +00:00
}
}
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Invalid ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2018-10-09 22:31:44 +03:00
TSubgoal BuildBoat : : whatToDoToAchieve ( )
{
if ( cb - > getPlayerRelations ( ai - > playerID , shipyard - > o - > tempOwner ) = = PlayerRelations : : ENEMIES )
{
return fh - > chooseSolution ( ai - > ah - > howToVisitObj ( shipyard - > o ) ) ;
}
if ( shipyard - > shipyardStatus ( ) ! = IShipyard : : GOOD )
{
throw cannotFulfillGoalException ( " Shipyard is busy. " ) ;
}
TResources boatCost ;
shipyard - > getBoatCost ( boatCost ) ;
return ai - > ah - > whatToDo ( boatCost , this - > iAmElementar ( ) ) ;
}
void BuildBoat : : accept ( VCAI * ai )
{
TResources boatCost ;
shipyard - > getBoatCost ( boatCost ) ;
if ( ! cb - > getResourceAmount ( ) . canAfford ( boatCost ) )
{
throw cannotFulfillGoalException ( " Can not afford boat " ) ;
}
if ( cb - > getPlayerRelations ( ai - > playerID , shipyard - > o - > tempOwner ) = = PlayerRelations : : ENEMIES )
{
throw cannotFulfillGoalException ( " Can not build boat in enemy shipyard " ) ;
}
if ( shipyard - > shipyardStatus ( ) ! = IShipyard : : GOOD )
{
throw cannotFulfillGoalException ( " Shipyard is busy. " ) ;
}
cb - > buildBoat ( shipyard ) ;
}
std : : string BuildBoat : : name ( ) const
{
return " BuildBoat " ;
}
std : : string BuildBoat : : completeMessage ( ) const
{
return " Boat have been built at " + shipyard - > o - > visitablePos ( ) . toString ( ) ;
}
2013-10-18 20:17:25 +00:00
TSubgoal FindObj : : whatToDoToAchieve ( )
{
const CGObjectInstance * o = nullptr ;
2018-04-07 15:44:14 +07:00
if ( resID > - 1 ) //specified
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
for ( const CGObjectInstance * obj : ai - > visitableObjs )
2013-10-18 20:17:25 +00:00
{
if ( obj - > ID = = objid & & obj - > subID = = resID )
{
o = obj ;
break ; //TODO: consider multiple objects and choose best
}
}
}
else
{
2018-04-07 15:44:14 +07:00
for ( const CGObjectInstance * obj : ai - > visitableObjs )
2013-10-18 20:17:25 +00:00
{
if ( obj - > ID = = objid )
{
o = obj ;
break ; //TODO: consider multiple objects and choose best
}
}
}
2018-04-07 15:44:14 +07:00
if ( o & & ai - > isAccessible ( o - > pos ) ) //we don't use isAccessibleForHero as we don't know which hero it is
2018-08-10 16:42:55 +02:00
return sptr ( Goals : : VisitObj ( o - > id . getNum ( ) ) ) ;
2013-10-18 20:17:25 +00:00
else
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Explore ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2013-12-20 09:43:12 +00:00
2018-07-26 12:06:55 +02:00
bool Goals : : FindObj : : fulfillsMe ( TSubgoal goal )
{
if ( goal - > goalType = = Goals : : VISIT_TILE ) //visiting tile visits object at same time
{
if ( ! hero | | hero = = goal - > hero )
for ( auto obj : cb - > getVisitableObjs ( goal - > tile ) ) //check if any object on that tile matches criteria
if ( obj - > visitablePos ( ) = = goal - > tile ) //object could be removed
if ( obj - > ID = = objid & & obj - > subID = = resID ) //same type and subtype
return true ;
}
return false ;
}
2018-08-10 16:42:55 +02:00
std : : string VisitObj : : completeMessage ( ) const
2013-11-24 08:21:51 +00:00
{
2016-01-19 20:25:15 +03:00
return " hero " + hero . get ( ) - > name + " captured Object ID = " + boost : : lexical_cast < std : : string > ( objid ) ;
2013-11-24 08:21:51 +00:00
}
2018-08-10 16:42:55 +02:00
TGoalVec VisitObj : : getAllPossibleSubgoals ( )
2013-10-18 20:17:25 +00:00
{
2018-08-07 14:33:45 +02:00
TGoalVec goalList ;
2018-08-10 00:33:18 +02:00
const CGObjectInstance * obj = cb - > getObjInstance ( ObjectInstanceID ( objid ) ) ;
2013-10-18 20:17:25 +00:00
if ( ! obj )
2018-08-07 14:33:45 +02:00
{
2018-08-10 13:42:15 +02:00
throw cannotFulfillGoalException ( " Object is missing - goal is invalid now! " ) ;
2018-08-07 14:33:45 +02:00
}
2014-12-21 11:33:53 +01:00
2013-10-18 20:17:25 +00:00
int3 pos = obj - > visitablePos ( ) ;
2018-04-07 15:44:14 +07:00
if ( hero )
2013-12-24 22:01:16 +00:00
{
2018-04-07 15:44:14 +07:00
if ( ai - > isAccessibleForHero ( pos , hero ) )
2018-08-07 00:32:12 +02:00
{
if ( isSafeToVisit ( hero , pos ) )
2018-08-10 16:42:55 +02:00
goalList . push_back ( sptr ( Goals : : VisitObj ( obj - > id . getNum ( ) ) . sethero ( hero ) ) ) ;
2018-08-07 00:32:12 +02:00
else
2018-08-07 14:33:45 +02:00
goalList . push_back ( sptr ( Goals : : GatherArmy ( evaluateDanger ( pos , hero . h ) * SAFE_ATTACK_CONSTANT ) . sethero ( hero ) . setisAbstract ( true ) ) ) ;
return goalList ;
2018-08-07 00:32:12 +02:00
}
2013-12-24 22:01:16 +00:00
}
else
{
2018-08-19 20:41:29 +02:00
for ( auto potentialVisitor : cb - > getHeroesInfo ( ) )
2018-08-07 14:33:45 +02:00
{
2018-08-19 20:41:29 +02:00
if ( ai - > isAccessibleForHero ( pos , potentialVisitor ) )
2018-08-07 00:32:12 +02:00
{
2018-08-19 20:41:29 +02:00
if ( isSafeToVisit ( potentialVisitor , pos ) )
goalList . push_back ( sptr ( Goals : : VisitObj ( obj - > id . getNum ( ) ) . sethero ( potentialVisitor ) ) ) ;
2018-08-07 00:32:12 +02:00
else
2018-08-19 20:41:29 +02:00
goalList . push_back ( sptr ( Goals : : GatherArmy ( evaluateDanger ( pos , potentialVisitor ) * SAFE_ATTACK_CONSTANT ) . sethero ( potentialVisitor ) . setisAbstract ( true ) ) ) ;
2018-08-07 00:32:12 +02:00
}
}
2018-08-07 14:33:45 +02:00
if ( ! goalList . empty ( ) )
2018-08-07 00:32:12 +02:00
{
2018-08-07 14:33:45 +02:00
return goalList ;
2014-12-21 11:33:53 +01:00
}
2013-12-24 22:01:16 +00:00
}
2018-08-07 14:33:45 +02:00
2018-08-19 20:41:29 +02:00
goalList . push_back ( sptr ( Goals : : ClearWayTo ( pos ) ) ) ;
2018-08-07 14:33:45 +02:00
return goalList ;
}
2018-08-10 16:42:55 +02:00
TSubgoal VisitObj : : whatToDoToAchieve ( )
2018-08-07 14:33:45 +02:00
{
auto bestGoal = fh - > chooseSolution ( getAllPossibleSubgoals ( ) ) ;
2018-08-10 16:48:42 +02:00
if ( bestGoal - > goalType = = Goals : : VISIT_OBJ & & bestGoal - > hero )
2018-08-07 14:33:45 +02:00
bestGoal - > setisElementar ( true ) ;
return bestGoal ;
2013-10-18 20:17:25 +00:00
}
2018-08-10 16:48:42 +02:00
Goals : : VisitObj : : VisitObj ( int Objid ) : CGoal ( Goals : : VISIT_OBJ )
2018-08-10 00:33:18 +02:00
{
objid = Objid ;
tile = ai - > myCb - > getObjInstance ( ObjectInstanceID ( objid ) ) - > visitablePos ( ) ;
priority = 3 ;
}
2018-08-10 16:42:55 +02:00
bool VisitObj : : fulfillsMe ( TSubgoal goal )
2013-11-24 19:15:08 +00:00
{
2018-08-10 13:42:15 +02:00
if ( goal - > goalType = = Goals : : VISIT_TILE )
2013-12-24 22:12:12 +00:00
{
2018-07-26 12:06:55 +02:00
if ( ! hero | | hero = = goal - > hero )
{
2018-08-10 00:33:18 +02:00
auto obj = cb - > getObjInstance ( ObjectInstanceID ( objid ) ) ;
2018-07-26 12:06:55 +02:00
if ( obj & & obj - > visitablePos ( ) = = goal - > tile ) //object could be removed
return true ;
}
2013-12-24 22:12:12 +00:00
}
2013-12-25 13:38:20 +00:00
return false ;
2013-11-24 19:15:08 +00:00
}
2013-11-24 08:21:51 +00:00
std : : string VisitHero : : completeMessage ( ) const
{
2016-01-19 20:25:15 +03:00
return " hero " + hero . get ( ) - > name + " visited hero " + boost : : lexical_cast < std : : string > ( objid ) ;
2013-11-24 08:21:51 +00:00
}
2013-10-18 20:17:25 +00:00
TSubgoal VisitHero : : whatToDoToAchieve ( )
{
const CGObjectInstance * obj = cb - > getObj ( ObjectInstanceID ( objid ) ) ;
if ( ! obj )
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Explore ( ) ) ;
2013-10-18 20:17:25 +00:00
int3 pos = obj - > visitablePos ( ) ;
2018-04-07 15:44:14 +07:00
if ( hero & & ai - > isAccessibleForHero ( pos , hero , true ) & & isSafeToVisit ( hero , pos ) ) //enemy heroes can get reinforcements
2013-11-15 17:30:16 +00:00
{
2018-04-07 15:44:14 +07:00
if ( hero - > pos = = pos )
2016-08-13 17:44:37 +03:00
logAi - > error ( " Hero %s tries to visit himself. " , hero . name ) ;
2013-12-21 17:34:59 +00:00
else
{
2013-12-27 13:20:40 +00:00
//can't use VISIT_TILE here as tile appears blocked by target hero
//FIXME: elementar goal should not be abstract
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : VisitHero ( objid ) . sethero ( hero ) . settile ( pos ) . setisElementar ( true ) ) ;
2013-12-21 17:34:59 +00:00
}
2013-11-15 17:30:16 +00:00
}
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Invalid ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2018-04-07 15:44:14 +07:00
bool VisitHero : : fulfillsMe ( TSubgoal goal )
2013-11-24 19:15:08 +00:00
{
2018-07-26 12:06:55 +02:00
//TODO: VisitObj shoudl not be used for heroes, but...
if ( goal - > goalType = = Goals : : VISIT_TILE )
2016-01-19 20:25:15 +03:00
{
2018-07-26 12:06:55 +02:00
auto obj = cb - > getObj ( ObjectInstanceID ( objid ) ) ;
if ( ! obj )
{
logAi - > error ( " Hero %s: VisitHero::fulfillsMe at %s: object %d not found " , hero . name , goal - > tile . toString ( ) , objid ) ;
return false ;
}
return obj - > visitablePos ( ) = = goal - > tile ;
2016-01-19 20:25:15 +03:00
}
2018-07-26 12:06:55 +02:00
return false ;
2013-11-24 19:15:08 +00:00
}
2013-10-18 20:17:25 +00:00
TSubgoal GetArtOfType : : whatToDoToAchieve ( )
{
TSubgoal alternativeWay = CGoal : : lookForArtSmart ( aid ) ; //TODO: use
2013-11-09 21:29:46 +00:00
if ( alternativeWay - > invalid ( ) )
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : FindObj ( Obj : : ARTIFACT , aid ) ) ;
return sptr ( Goals : : Invalid ( ) ) ;
2013-10-18 20:17:25 +00:00
}
TSubgoal ClearWayTo : : whatToDoToAchieve ( )
{
2013-12-23 20:46:01 +00:00
assert ( cb - > isInTheMap ( tile ) ) ; //set tile
2013-10-18 20:17:25 +00:00
if ( ! cb - > isVisible ( tile ) )
{
2016-08-13 17:44:37 +03:00
logAi - > error ( " Clear way should be used with visible tiles! " ) ;
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Explore ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2016-01-19 20:25:15 +03:00
return ( fh - > chooseSolution ( getAllPossibleSubgoals ( ) ) ) ;
2013-12-23 20:46:01 +00:00
}
2013-10-18 20:17:25 +00:00
2018-07-26 12:06:55 +02:00
bool Goals : : ClearWayTo : : fulfillsMe ( TSubgoal goal )
{
if ( goal - > goalType = = Goals : : VISIT_TILE )
2018-07-27 09:22:07 +02:00
{
2018-07-26 12:06:55 +02:00
if ( ! hero | | hero = = goal - > hero )
return tile = = goal - > tile ;
2018-07-27 09:22:07 +02:00
}
2018-07-26 12:06:55 +02:00
return false ;
}
2013-12-23 20:46:01 +00:00
TGoalVec ClearWayTo : : getAllPossibleSubgoals ( )
{
TGoalVec ret ;
2014-02-20 20:18:49 +00:00
std : : vector < const CGHeroInstance * > heroes ;
2018-04-07 15:44:14 +07:00
if ( hero )
2014-02-20 20:18:49 +00:00
heroes . push_back ( hero . h ) ;
else
heroes = cb - > getHeroesInfo ( ) ;
2013-12-26 09:53:37 +00:00
2018-04-07 15:44:14 +07:00
for ( auto h : heroes )
2014-02-20 20:18:49 +00:00
{
//TODO: handle clearing way to allied heroes that are blocked
//if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
// h->visitablePos() == tile) //we are already on that tile! what does it mean?
// continue;
2013-10-18 20:17:25 +00:00
2013-12-23 20:46:01 +00:00
//if our hero is trapped, make sure we request clearing the way from OUR perspective
2014-02-20 20:18:49 +00:00
2018-08-12 14:31:31 +03:00
vstd : : concatenate ( ret , ai - > ah - > howToVisitTile ( h , tile ) ) ;
2013-12-23 20:46:01 +00:00
}
2018-08-12 14:31:31 +03:00
if ( ret . empty ( ) & & ai - > canRecruitAnyHero ( ) )
2018-04-07 15:44:14 +07:00
ret . push_back ( sptr ( Goals : : RecruitHero ( ) ) ) ;
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
if ( ret . empty ( ) )
2013-12-24 22:01:16 +00:00
{
2017-08-11 20:03:05 +03:00
logAi - > warn ( " There is no known way to clear the way to tile %s " , tile . toString ( ) ) ;
2018-04-07 15:44:14 +07:00
throw goalFulfilledException ( sptr ( Goals : : ClearWayTo ( tile ) ) ) ; //make sure asigned hero gets unlocked
2013-12-24 22:01:16 +00:00
}
2013-10-18 20:17:25 +00:00
2013-12-23 20:46:01 +00:00
return ret ;
2013-10-18 20:17:25 +00:00
}
2013-11-24 08:21:51 +00:00
std : : string Explore : : completeMessage ( ) const
{
return " Hero " + hero . get ( ) - > name + " completed exploration " ;
2014-01-16 20:24:06 +00:00
}
2013-11-24 08:21:51 +00:00
2013-10-18 20:17:25 +00:00
TSubgoal Explore : : whatToDoToAchieve ( )
{
2013-12-20 16:49:51 +00:00
auto ret = fh - > chooseSolution ( getAllPossibleSubgoals ( ) ) ;
2018-04-07 15:44:14 +07:00
if ( hero ) //use best step for this hero
{
2013-12-20 16:49:51 +00:00
return ret ;
2018-04-07 15:44:14 +07:00
}
2013-12-20 16:49:51 +00:00
else
{
2018-04-07 15:44:14 +07:00
if ( ret - > hero . get ( true ) )
return sptr ( sethero ( ret - > hero . h ) . setisAbstract ( true ) ) ; //choose this hero and then continue with him
2013-12-20 16:49:51 +00:00
else
return ret ; //other solutions, like buying hero from tavern
}
2014-01-16 20:24:06 +00:00
}
2013-12-20 16:49:51 +00:00
TGoalVec Explore : : getAllPossibleSubgoals ( )
{
TGoalVec ret ;
2013-12-23 20:46:01 +00:00
std : : vector < const CGHeroInstance * > heroes ;
2014-02-19 16:23:47 +00:00
2018-04-07 15:44:14 +07:00
if ( hero )
{
2013-12-23 20:46:01 +00:00
heroes . push_back ( hero . h ) ;
2018-04-07 15:44:14 +07:00
}
2013-12-20 16:49:51 +00:00
else
{
2013-12-23 20:46:01 +00:00
//heroes = ai->getUnblockedHeroes();
heroes = cb - > getHeroesInfo ( ) ;
2015-12-29 02:14:08 +03:00
vstd : : erase_if ( heroes , [ ] ( const HeroPtr h )
2013-12-20 16:49:51 +00:00
{
2018-04-07 15:44:14 +07:00
if ( ai - > getGoal ( h ) - > goalType = = Goals : : EXPLORE ) //do not reassign hero who is already explorer
2014-02-19 16:23:47 +00:00
return true ;
2014-02-17 07:36:03 +00:00
2018-04-07 15:44:14 +07:00
if ( ! ai - > isAbleToExplore ( h ) )
2014-03-23 16:00:43 +00:00
return true ;
2013-12-20 21:10:43 +00:00
return ! h - > movement ; //saves time, immobile heroes are useless anyway
2013-12-20 16:49:51 +00:00
} ) ;
}
2013-12-21 17:34:59 +00:00
//try to use buildings that uncover map
std : : vector < const CGObjectInstance * > objs ;
2018-04-07 15:44:14 +07:00
for ( auto obj : ai - > visitableObjs )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
if ( ! vstd : : contains ( ai - > alreadyVisited , obj ) )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
switch ( obj - > ID . num )
2013-12-21 17:34:59 +00:00
{
2015-03-09 01:25:52 +03:00
case Obj : : REDWOOD_OBSERVATORY :
case Obj : : PILLAR_OF_FIRE :
case Obj : : CARTOGRAPHER :
2018-04-07 15:44:14 +07:00
objs . push_back ( obj ) ;
2015-03-09 01:25:52 +03:00
break ;
case Obj : : MONOLITH_ONE_WAY_ENTRANCE :
case Obj : : MONOLITH_TWO_WAY :
case Obj : : SUBTERRANEAN_GATE :
auto tObj = dynamic_cast < const CGTeleport * > ( obj ) ;
assert ( ai - > knownTeleportChannels . find ( tObj - > channel ) ! = ai - > knownTeleportChannels . end ( ) ) ;
if ( TeleportChannel : : IMPASSABLE ! = ai - > knownTeleportChannels [ tObj - > channel ] - > passability )
2018-04-07 15:44:14 +07:00
objs . push_back ( obj ) ;
2015-03-09 01:25:52 +03:00
break ;
2015-03-08 17:49:14 +03:00
}
}
else
{
2018-04-07 15:44:14 +07:00
switch ( obj - > ID . num )
2015-03-08 17:49:14 +03:00
{
2015-03-09 01:25:52 +03:00
case Obj : : MONOLITH_TWO_WAY :
case Obj : : SUBTERRANEAN_GATE :
auto tObj = dynamic_cast < const CGTeleport * > ( obj ) ;
if ( TeleportChannel : : IMPASSABLE = = ai - > knownTeleportChannels [ tObj - > channel ] - > passability )
break ;
for ( auto exit : ai - > knownTeleportChannels [ tObj - > channel ] - > exits )
2015-03-08 17:49:14 +03:00
{
2015-03-09 01:25:52 +03:00
if ( ! cb - > getObj ( exit ) )
{ // Always attempt to visit two-way teleports if one of channel exits is not visible
objs . push_back ( obj ) ;
2015-03-08 17:49:14 +03:00
break ;
}
}
2015-03-09 01:25:52 +03:00
break ;
2013-12-21 17:34:59 +00:00
}
2013-10-18 20:17:25 +00:00
}
2013-12-21 17:34:59 +00:00
}
2015-03-30 15:32:23 +02:00
2018-07-26 16:15:37 +02:00
auto primaryHero = ai - > primaryHero ( ) . h ;
2018-04-07 15:44:14 +07:00
for ( auto h : heroes )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
for ( auto obj : objs ) //double loop, performance risk?
2013-10-18 20:17:25 +00:00
{
2018-08-12 14:31:31 +03:00
auto waysToVisitObj = ai - > ah - > howToVisitObj ( h , obj ) ;
vstd : : concatenate ( ret , waysToVisitObj ) ;
2013-10-18 20:17:25 +00:00
}
2013-12-20 16:49:51 +00:00
int3 t = whereToExplore ( h ) ;
2018-04-07 15:44:14 +07:00
if ( t . valid ( ) )
2014-02-15 21:32:49 +00:00
{
2018-04-07 15:44:14 +07:00
ret . push_back ( sptr ( Goals : : VisitTile ( t ) . sethero ( h ) ) ) ;
2014-02-15 21:32:49 +00:00
}
2014-03-23 16:00:43 +00:00
else
2014-02-17 07:36:03 +00:00
{
2018-07-26 16:15:37 +02:00
//FIXME: possible issues when gathering army to break
if ( hero . h = = h | | //exporation is assigned to this hero
( ! hero & & h = = primaryHero ) ) //not assigned to specific hero, let our main hero do the job
2014-03-23 16:00:43 +00:00
{
2018-07-26 16:15:37 +02:00
t = ai - > explorationDesperate ( h ) ; //check this only ONCE, high cost
if ( t . valid ( ) ) //don't waste time if we are completely blocked
{
2018-08-12 14:31:31 +03:00
auto waysToVisitTile = ai - > ah - > howToVisitTile ( h , t ) ;
vstd : : concatenate ( ret , waysToVisitTile ) ;
2018-07-26 16:15:37 +02:00
continue ;
}
2014-03-23 16:00:43 +00:00
}
2018-07-26 16:15:37 +02:00
ai - > markHeroUnableToExplore ( h ) ; //there is no freely accessible tile, do not poll this hero anymore
2014-02-17 07:36:03 +00:00
}
2013-10-18 20:17:25 +00:00
}
2013-12-23 20:46:01 +00:00
//we either don't have hero yet or none of heroes can explore
2018-04-07 15:44:14 +07:00
if ( ( ! hero | | ret . empty ( ) ) & & ai - > canRecruitAnyHero ( ) )
ret . push_back ( sptr ( Goals : : RecruitHero ( ) ) ) ;
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
if ( ret . empty ( ) )
2013-12-26 09:53:37 +00:00
{
2018-04-07 15:44:14 +07:00
throw goalFulfilledException ( sptr ( Goals : : Explore ( ) . sethero ( hero ) ) ) ;
2013-12-26 09:53:37 +00:00
}
//throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
2013-10-18 20:17:25 +00:00
2013-12-20 16:49:51 +00:00
return ret ;
2014-01-16 20:24:06 +00:00
}
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
bool Explore : : fulfillsMe ( TSubgoal goal )
2013-12-26 09:53:37 +00:00
{
2018-04-07 15:44:14 +07:00
if ( goal - > goalType = = Goals : : EXPLORE )
2013-12-26 09:53:37 +00:00
{
2018-04-07 15:44:14 +07:00
if ( goal - > hero )
2013-12-26 09:53:37 +00:00
return hero = = goal - > hero ;
else
return true ; //cancel ALL exploration
}
return false ;
}
2013-10-18 20:17:25 +00:00
TSubgoal RecruitHero : : whatToDoToAchieve ( )
{
2018-04-07 15:44:14 +07:00
const CGTownInstance * t = ai - > findTownWithTavern ( ) ;
2013-10-18 20:17:25 +00:00
if ( ! t )
2018-07-26 12:06:55 +02:00
return sptr ( Goals : : BuildThis ( BuildingID : : TAVERN ) . setpriority ( 2 ) ) ;
2013-10-18 20:17:25 +00:00
2018-07-26 12:06:55 +02:00
TResources res ;
res [ Res : : GOLD ] = GameConstants : : HERO_GOLD_COST ;
2018-09-02 18:04:38 +03:00
return ai - > ah - > whatToDo ( res , iAmElementar ( ) ) ; //either buy immediately, or collect res
2018-07-26 12:06:55 +02:00
}
2013-10-18 20:17:25 +00:00
2013-11-24 08:21:51 +00:00
std : : string VisitTile : : completeMessage ( ) const
{
2017-08-11 20:03:05 +03:00
return " Hero " + hero . get ( ) - > name + " visited tile " + tile . toString ( ) ;
2013-11-24 08:21:51 +00:00
}
2013-10-18 20:17:25 +00:00
TSubgoal VisitTile : : whatToDoToAchieve ( )
{
2013-12-20 13:07:58 +00:00
auto ret = fh - > chooseSolution ( getAllPossibleSubgoals ( ) ) ;
2013-10-18 20:17:25 +00:00
2017-07-15 00:15:08 +02:00
if ( ret - > hero )
2013-10-18 20:17:25 +00:00
{
2017-07-15 00:15:08 +02:00
if ( isSafeToVisit ( ret - > hero , tile ) & & ai - > isAccessibleForHero ( tile , ret - > hero ) )
2013-10-18 20:17:25 +00:00
{
2013-12-20 09:43:12 +00:00
ret - > setisElementar ( true ) ;
return ret ;
2013-10-18 20:17:25 +00:00
}
else
{
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : GatherArmy ( evaluateDanger ( tile , * ret - > hero ) * SAFE_ATTACK_CONSTANT )
. sethero ( ret - > hero ) . setisAbstract ( true ) ) ;
2013-10-18 20:17:25 +00:00
}
}
2013-12-20 09:43:12 +00:00
return ret ;
}
2013-12-19 20:21:21 +00:00
TGoalVec VisitTile : : getAllPossibleSubgoals ( )
{
2014-02-15 21:32:49 +00:00
assert ( cb - > isInTheMap ( tile ) ) ;
2013-12-19 20:21:21 +00:00
TGoalVec ret ;
2018-04-07 15:44:14 +07:00
if ( ! cb - > isVisible ( tile ) )
ret . push_back ( sptr ( Goals : : Explore ( ) ) ) ; //what sense does it make?
2013-12-19 20:21:21 +00:00
else
{
2013-12-21 07:37:23 +00:00
std : : vector < const CGHeroInstance * > heroes ;
2018-04-07 15:44:14 +07:00
if ( hero )
2013-12-21 07:37:23 +00:00
heroes . push_back ( hero . h ) ; //use assigned hero if any
else
heroes = cb - > getHeroesInfo ( ) ; //use most convenient hero
2018-04-07 15:44:14 +07:00
for ( auto h : heroes )
2013-12-19 20:21:21 +00:00
{
2018-04-07 15:44:14 +07:00
if ( ai - > isAccessibleForHero ( tile , h ) )
ret . push_back ( sptr ( Goals : : VisitTile ( tile ) . sethero ( h ) ) ) ;
2013-12-19 20:21:21 +00:00
}
2018-04-07 15:44:14 +07:00
if ( ai - > canRecruitAnyHero ( ) )
ret . push_back ( sptr ( Goals : : RecruitHero ( ) ) ) ;
2013-12-19 20:21:21 +00:00
}
2015-12-29 02:14:08 +03:00
if ( ret . empty ( ) )
2013-12-26 09:53:37 +00:00
{
2015-12-29 02:14:08 +03:00
auto obj = vstd : : frontOrNull ( cb - > getVisitableObjs ( tile ) ) ;
if ( obj & & obj - > ID = = Obj : : HERO & & obj - > tempOwner = = ai - > playerID ) //our own hero stands on that tile
2014-12-21 14:50:26 +01:00
{
2018-04-07 15:44:14 +07:00
if ( hero . get ( true ) & & hero - > id = = obj - > id ) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
2014-12-21 14:50:26 +01:00
ret . push_back ( sptr ( Goals : : VisitTile ( tile ) . sethero ( dynamic_cast < const CGHeroInstance * > ( obj ) ) . setisElementar ( true ) ) ) ;
else
throw cannotFulfillGoalException ( " Tile is already occupied by another hero " ) ; //FIXME: we should give up this tile earlier
}
2013-12-26 09:53:37 +00:00
else
2018-04-07 15:44:14 +07:00
ret . push_back ( sptr ( Goals : : ClearWayTo ( tile ) ) ) ;
2013-12-26 09:53:37 +00:00
}
2013-12-23 20:46:01 +00:00
2013-12-20 09:43:12 +00:00
//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
2013-12-19 20:21:21 +00:00
return ret ;
}
2013-10-18 20:17:25 +00:00
TSubgoal DigAtTile : : whatToDoToAchieve ( )
{
2018-04-07 15:44:14 +07:00
const CGObjectInstance * firstObj = vstd : : frontOrNull ( cb - > getVisitableObjs ( tile ) ) ;
2013-10-18 20:17:25 +00:00
if ( firstObj & & firstObj - > ID = = Obj : : HERO & & firstObj - > tempOwner = = ai - > playerID ) //we have hero at dest
{
2018-04-07 15:44:14 +07:00
const CGHeroInstance * h = dynamic_cast < const CGHeroInstance * > ( firstObj ) ;
2013-11-15 17:30:16 +00:00
sethero ( h ) . setisElementar ( true ) ;
2018-04-07 15:44:14 +07:00
return sptr ( * this ) ;
2013-10-18 20:17:25 +00:00
}
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : VisitTile ( tile ) ) ;
2013-10-18 20:17:25 +00:00
}
TSubgoal BuildThis : : whatToDoToAchieve ( )
{
2018-07-26 12:06:55 +02:00
auto b = BuildingID ( bid ) ;
// find town if not set
if ( ! town & & hero )
town = hero - > visitedTown ;
if ( ! town )
{
for ( const CGTownInstance * t : cb - > getTownsInfo ( ) )
{
switch ( cb - > canBuildStructure ( town , b ) )
{
case EBuildingState : : ALLOWED :
town = t ;
break ; //TODO: look for prerequisites? this is not our reponsibility
default :
continue ;
}
}
}
if ( town ) //we have specific town to build this
{
2018-08-21 12:55:31 +02:00
switch ( cb - > canBuildStructure ( town , b ) )
2018-08-05 09:21:41 +02:00
{
2018-08-21 12:55:31 +02:00
case EBuildingState : : ALLOWED :
case EBuildingState : : NO_RESOURCES :
{
auto res = town - > town - > buildings . at ( BuildingID ( bid ) ) - > resources ;
2018-09-02 18:04:38 +03:00
return ai - > ah - > whatToDo ( res , iAmElementar ( ) ) ; //realize immediately or gather resources
2018-08-21 12:55:31 +02:00
}
break ;
default :
throw cannotFulfillGoalException ( " Not possible to build " ) ;
2018-08-05 09:21:41 +02:00
}
2018-07-26 12:06:55 +02:00
}
else
throw cannotFulfillGoalException ( " Cannot find town to build this " ) ;
}
TGoalVec Goals : : CollectRes : : getAllPossibleSubgoals ( )
{
TGoalVec ret ;
auto givesResource = [ this ] ( const CGObjectInstance * obj ) - > bool
{
//TODO: move this logic to object side
//TODO: remember mithril exists
//TODO: water objects
//TODO: Creature banks
//return false first from once-visitable, before checking if they were even visited
switch ( obj - > ID . num )
{
case Obj : : TREASURE_CHEST :
return resID = = Res : : GOLD ;
break ;
case Obj : : RESOURCE :
return obj - > subID = = resID ;
break ;
case Obj : : MINE :
return ( obj - > subID = = resID & &
( cb - > getPlayerRelations ( obj - > tempOwner , ai - > playerID ) = = PlayerRelations : : ENEMIES ) ) ; //don't capture our mines
break ;
case Obj : : CAMPFIRE :
return true ; //contains all resources
break ;
case Obj : : WINDMILL :
switch ( resID )
{
case Res : : GOLD :
case Res : : WOOD :
return false ;
}
break ;
case Obj : : WATER_WHEEL :
if ( resID ! = Res : : GOLD )
return false ;
break ;
case Obj : : MYSTICAL_GARDEN :
if ( ( resID ! = Res : : GOLD ) & & ( resID ! = Res : : GEMS ) )
return false ;
break ;
case Obj : : LEAN_TO :
case Obj : : WAGON :
if ( resID ! = Res : : GOLD )
return false ;
break ;
default :
return false ;
break ;
}
return ! vstd : : contains ( ai - > alreadyVisited , obj ) ; //for weekly / once visitable
} ;
std : : vector < const CGObjectInstance * > objs ;
for ( auto obj : ai - > visitableObjs )
{
if ( givesResource ( obj ) )
objs . push_back ( obj ) ;
}
for ( auto h : cb - > getHeroesInfo ( ) )
{
std : : vector < const CGObjectInstance * > ourObjs ( objs ) ; //copy common objects
for ( auto obj : ai - > reservedHeroesMap [ h ] ) //add objects reserved by this hero
{
if ( givesResource ( obj ) )
ourObjs . push_back ( obj ) ;
}
2018-08-12 14:31:31 +03:00
2018-07-26 12:06:55 +02:00
for ( auto obj : ourObjs )
{
2018-08-12 14:31:31 +03:00
auto waysToGo = ai - > ah - > howToVisitObj ( h , ObjectIdRef ( obj ) ) ;
vstd : : concatenate ( ret , waysToGo ) ;
2018-07-26 12:06:55 +02:00
}
}
return ret ;
2013-10-18 20:17:25 +00:00
}
TSubgoal CollectRes : : whatToDoToAchieve ( )
2018-07-29 22:07:40 +03:00
{
2018-07-26 12:06:55 +02:00
auto goals = getAllPossibleSubgoals ( ) ;
auto trade = whatToDoToTrade ( ) ;
if ( ! trade - > invalid ( ) )
goals . push_back ( trade ) ;
if ( goals . empty ( ) )
return sptr ( Goals : : Explore ( ) ) ; //we can always do that
else
return fh - > chooseSolution ( goals ) ; //TODO: evaluate trading
}
TSubgoal Goals : : CollectRes : : whatToDoToTrade ( )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
std : : vector < const IMarket * > markets ;
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
std : : vector < const CGObjectInstance * > visObjs ;
2018-02-10 21:52:23 +03:00
ai - > retrieveVisitableObjs ( visObjs , true ) ;
2018-07-26 12:06:55 +02:00
for ( const CGObjectInstance * obj : visObjs )
2013-10-18 20:17:25 +00:00
{
2018-07-26 12:06:55 +02:00
if ( const IMarket * m = IMarket : : castFrom ( obj , false ) )
2013-10-18 20:17:25 +00:00
{
2018-07-26 12:06:55 +02:00
if ( obj - > ID = = Obj : : TOWN & & obj - > tempOwner = = ai - > playerID & & m - > allowsTrade ( EMarketMode : : RESOURCE_RESOURCE ) )
2013-10-18 20:17:25 +00:00
markets . push_back ( m ) ;
2018-07-26 12:06:55 +02:00
else if ( obj - > ID = = Obj : : TRADING_POST )
2013-10-18 20:17:25 +00:00
markets . push_back ( m ) ;
}
}
2018-04-07 15:44:14 +07:00
boost : : sort ( markets , [ ] ( const IMarket * m1 , const IMarket * m2 ) - > bool
2013-10-18 20:17:25 +00:00
{
return m1 - > getMarketEfficiency ( ) < m2 - > getMarketEfficiency ( ) ;
} ) ;
2018-04-07 15:44:14 +07:00
markets . erase ( boost : : remove_if ( markets , [ ] ( const IMarket * market ) - > bool
2013-10-18 20:17:25 +00:00
{
2018-07-26 12:06:55 +02:00
if ( ! ( market - > o - > ID = = Obj : : TOWN & & market - > o - > tempOwner = = ai - > playerID ) )
2018-04-07 15:44:14 +07:00
{
2018-07-26 12:06:55 +02:00
if ( ! ai - > isAccessible ( market - > o - > visitablePos ( ) ) )
2018-04-07 15:44:14 +07:00
return true ;
}
return false ;
} ) , markets . end ( ) ) ;
2013-10-18 20:17:25 +00:00
2018-07-26 12:06:55 +02:00
if ( ! markets . size ( ) )
2013-10-18 20:17:25 +00:00
{
2018-07-26 12:06:55 +02:00
for ( const CGTownInstance * t : cb - > getTownsInfo ( ) )
2013-10-18 20:17:25 +00:00
{
2018-07-26 12:06:55 +02:00
if ( cb - > canBuildStructure ( t , BuildingID : : MARKETPLACE ) = = EBuildingState : : ALLOWED )
return sptr ( Goals : : BuildThis ( BuildingID : : MARKETPLACE , t ) . setpriority ( 2 ) ) ;
2013-10-18 20:17:25 +00:00
}
}
else
{
2018-04-07 15:44:14 +07:00
const IMarket * m = markets . back ( ) ;
2013-10-18 20:17:25 +00:00
//attempt trade at back (best prices)
int howManyCanWeBuy = 0 ;
2018-07-26 12:06:55 +02:00
for ( Res : : ERes i = Res : : WOOD ; i < = Res : : GOLD ; vstd : : advance ( i , 1 ) )
2013-10-18 20:17:25 +00:00
{
2018-07-26 12:06:55 +02:00
if ( i = = resID )
2018-04-07 15:44:14 +07:00
continue ;
2013-10-18 20:17:25 +00:00
int toGive = - 1 , toReceive = - 1 ;
m - > getOffer ( i , resID , toGive , toReceive , EMarketMode : : RESOURCE_RESOURCE ) ;
assert ( toGive > 0 & & toReceive > 0 ) ;
2018-09-02 18:04:38 +03:00
howManyCanWeBuy + = toReceive * ( ai - > ah - > freeResources ( ) [ i ] / toGive ) ;
2013-10-18 20:17:25 +00:00
}
2018-07-26 12:06:55 +02:00
if ( howManyCanWeBuy > = value )
2013-10-18 20:17:25 +00:00
{
2014-02-17 17:28:39 +00:00
auto backObj = cb - > getTopObj ( m - > o - > visitablePos ( ) ) ; //it'll be a hero if we have one there; otherwise marketplace
2013-10-18 20:17:25 +00:00
assert ( backObj ) ;
2018-07-26 12:06:55 +02:00
auto objid = m - > o - > id . getNum ( ) ;
2018-07-28 09:08:53 +02:00
if ( backObj - > tempOwner ! = ai - > playerID ) //top object not owned
2013-11-15 17:30:16 +00:00
{
2018-08-10 16:42:55 +02:00
return sptr ( Goals : : VisitObj ( objid ) ) ; //just go there
2013-11-15 17:30:16 +00:00
}
2018-07-28 09:08:53 +02:00
else //either it's our town, or we have hero there
2013-11-15 17:30:16 +00:00
{
2018-08-04 10:20:40 +02:00
Goals : : Trade trade ( resID , value , objid ) ;
2018-08-21 12:55:31 +02:00
return sptr ( trade . setisElementar ( true ) ) ; //we can do this immediately
2013-11-15 17:30:16 +00:00
}
2013-10-18 20:17:25 +00:00
}
}
2018-07-26 12:06:55 +02:00
return sptr ( Goals : : Invalid ( ) ) ; //cannot trade
}
bool CollectRes : : fulfillsMe ( TSubgoal goal )
{
if ( goal - > resID = = resID )
if ( goal - > value > = value )
return true ;
return false ;
2013-10-18 20:17:25 +00:00
}
TSubgoal GatherTroops : : whatToDoToAchieve ( )
{
std : : vector < const CGDwelling * > dwellings ;
2018-04-07 15:44:14 +07:00
for ( const CGTownInstance * t : cb - > getTownsInfo ( ) )
2013-10-18 20:17:25 +00:00
{
auto creature = VLC - > creh - > creatures [ objid ] ;
2018-04-07 15:44:14 +07:00
if ( t - > subID = = creature - > faction ) //TODO: how to force AI to build unupgraded creatures? :O
2013-10-18 20:17:25 +00:00
{
auto creatures = vstd : : tryAt ( t - > town - > creatures , creature - > level - 1 ) ;
if ( ! creatures )
2016-01-19 20:25:15 +03:00
continue ;
2013-10-18 20:17:25 +00:00
int upgradeNumber = vstd : : find_pos ( * creatures , creature - > idNumber ) ;
if ( upgradeNumber < 0 )
continue ;
BuildingID bid ( BuildingID : : DWELL_FIRST + creature - > level - 1 + upgradeNumber * GameConstants : : CREATURES_PER_TOWN ) ;
2018-04-07 15:44:14 +07:00
if ( t - > hasBuilt ( bid ) ) //this assumes only creatures with dwellings are assigned to faction
2013-10-18 20:17:25 +00:00
{
dwellings . push_back ( t ) ;
}
2018-10-12 20:33:15 +02:00
/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
{
2018-07-26 12:06:55 +02:00
return sptr ( Goals : : BuildThis ( bid , t ) . setpriority ( priority ) ) ;
2018-10-12 20:33:15 +02:00
} */
2013-10-18 20:17:25 +00:00
}
}
2018-04-07 15:44:14 +07:00
for ( auto obj : ai - > visitableObjs )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
if ( obj - > ID ! = Obj : : CREATURE_GENERATOR1 ) //TODO: what with other creature generators?
2013-10-18 20:17:25 +00:00
continue ;
auto d = dynamic_cast < const CGDwelling * > ( obj ) ;
2018-04-07 15:44:14 +07:00
for ( auto creature : d - > creatures )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
if ( creature . first ) //there are more than 0 creatures avaliabe
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
for ( auto type : creature . second )
2013-10-18 20:17:25 +00:00
{
2018-09-02 18:04:38 +03:00
if ( type = = objid & & ai - > ah - > freeResources ( ) . canAfford ( VLC - > creh - > creatures [ type ] - > cost ) )
2013-10-18 20:17:25 +00:00
dwellings . push_back ( d ) ;
}
}
}
}
2018-04-07 15:44:14 +07:00
if ( dwellings . size ( ) )
2013-10-18 20:17:25 +00:00
{
2014-09-21 18:08:47 +03:00
typedef std : : map < const CGHeroInstance * , const CGDwelling * > TDwellMap ;
// sorted helper
auto comparator = [ ] ( const TDwellMap : : value_type & a , const TDwellMap : : value_type & b ) - > bool
{
2018-04-07 15:44:14 +07:00
const CGPathNode * ln = ai - > myCb - > getPathsInfo ( a . first ) - > getPathInfo ( a . second - > visitablePos ( ) ) ;
const CGPathNode * rn = ai - > myCb - > getPathsInfo ( b . first ) - > getPathInfo ( b . second - > visitablePos ( ) ) ;
2014-09-21 18:08:47 +03:00
if ( ln - > turns ! = rn - > turns )
return ln - > turns < rn - > turns ;
return ( ln - > moveRemains > rn - > moveRemains ) ;
} ;
// for all owned heroes generate map <hero -> nearest dwelling>
TDwellMap nearestDwellings ;
2018-04-07 15:44:14 +07:00
for ( const CGHeroInstance * hero : cb - > getHeroesInfo ( true ) )
2014-09-21 18:08:47 +03:00
{
nearestDwellings [ hero ] = * boost : : range : : min_element ( dwellings , CDistanceSorter ( hero ) ) ;
}
2018-04-07 15:44:14 +07:00
if ( nearestDwellings . size ( ) )
2016-11-29 22:07:35 +01:00
{
// find hero who is nearest to a dwelling
const CGDwelling * nearest = boost : : range : : min_element ( nearestDwellings , comparator ) - > second ;
2018-04-07 15:44:14 +07:00
if ( ! nearest )
2016-11-29 22:07:35 +01:00
throw cannotFulfillGoalException ( " Cannot find nearest dwelling! " ) ;
2014-09-21 18:08:47 +03:00
2018-08-10 16:42:55 +02:00
return sptr ( Goals : : VisitObj ( nearest - > id . getNum ( ) ) ) ;
2016-11-29 22:07:35 +01:00
}
else
return sptr ( Goals : : Explore ( ) ) ;
2013-10-18 20:17:25 +00:00
}
else
2018-04-07 15:44:14 +07:00
{
return sptr ( Goals : : Explore ( ) ) ;
}
2013-10-18 20:17:25 +00:00
//TODO: exchange troops between heroes
}
2018-07-26 12:06:55 +02:00
bool Goals : : GatherTroops : : fulfillsMe ( TSubgoal goal )
{
if ( ! hero | | hero = = goal - > hero ) //we got army for desired hero or any hero
if ( goal - > objid = = objid ) //same creature type //TODO: consider upgrades?
if ( goal - > value > = value ) //notify every time we get resources?
return true ;
return false ;
}
2013-10-18 20:17:25 +00:00
TSubgoal Conquer : : whatToDoToAchieve ( )
{
2018-04-07 15:44:14 +07:00
return fh - > chooseSolution ( getAllPossibleSubgoals ( ) ) ;
2013-12-20 13:01:44 +00:00
}
TGoalVec Conquer : : getAllPossibleSubgoals ( )
{
TGoalVec ret ;
2013-10-18 20:17:25 +00:00
2014-02-17 07:36:03 +00:00
auto conquerable = [ ] ( const CGObjectInstance * obj ) - > bool
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
if ( cb - > getPlayerRelations ( ai - > playerID , obj - > tempOwner ) = = PlayerRelations : : ENEMIES )
2014-02-15 16:38:51 +00:00
{
2018-04-07 15:44:14 +07:00
switch ( obj - > ID . num )
2014-02-15 16:38:51 +00:00
{
2018-04-07 15:44:14 +07:00
case Obj : : TOWN :
case Obj : : HERO :
case Obj : : CREATURE_GENERATOR1 :
case Obj : : MINE : //TODO: check ai->knownSubterraneanGates
return true ;
2014-02-15 16:38:51 +00:00
}
}
2014-02-17 07:36:03 +00:00
return false ;
} ;
std : : vector < const CGObjectInstance * > objs ;
2017-07-15 00:15:08 +02:00
for ( auto obj : ai - > visitableObjs )
2014-02-17 07:36:03 +00:00
{
2017-07-15 00:15:08 +02:00
if ( conquerable ( obj ) )
2018-04-07 15:44:14 +07:00
objs . push_back ( obj ) ;
2014-02-15 16:38:51 +00:00
}
2013-10-18 20:17:25 +00:00
2017-07-15 00:15:08 +02:00
for ( auto h : cb - > getHeroesInfo ( ) )
2013-10-18 20:17:25 +00:00
{
2014-02-17 07:36:03 +00:00
std : : vector < const CGObjectInstance * > ourObjs ( objs ) ; //copy common objects
2017-07-15 00:15:08 +02:00
for ( auto obj : ai - > reservedHeroesMap [ h ] ) //add objects reserved by this hero
2014-02-17 07:36:03 +00:00
{
2017-07-15 00:15:08 +02:00
if ( conquerable ( obj ) )
2014-02-17 07:36:03 +00:00
ourObjs . push_back ( obj ) ;
}
2017-07-15 00:15:08 +02:00
for ( auto obj : ourObjs )
2013-10-18 20:17:25 +00:00
{
2018-08-12 14:31:31 +03:00
auto waysToGo = ai - > ah - > howToVisitObj ( h , ObjectIdRef ( obj ) ) ;
vstd : : concatenate ( ret , waysToGo ) ;
2013-10-18 20:17:25 +00:00
}
}
2018-04-07 15:44:14 +07:00
if ( ! objs . empty ( ) & & ai - > canRecruitAnyHero ( ) ) //probably no point to recruit hero if we see no objects to capture
ret . push_back ( sptr ( Goals : : RecruitHero ( ) ) ) ;
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
if ( ret . empty ( ) )
ret . push_back ( sptr ( Goals : : Explore ( ) ) ) ; //we need to find an enemy
2013-12-20 13:01:44 +00:00
return ret ;
2013-10-18 20:17:25 +00:00
}
2013-12-20 13:01:44 +00:00
2018-08-05 09:21:41 +02:00
TGoalVec Goals : : Build : : getAllPossibleSubgoals ( )
{
TGoalVec ret ;
for ( const CGTownInstance * t : cb - > getTownsInfo ( ) )
{
//start fresh with every town
2018-09-02 18:04:38 +03:00
ai - > ah - > getBuildingOptions ( t ) ;
2018-10-13 21:15:46 +02:00
auto immediateBuilding = ai - > ah - > immediateBuilding ( ) ;
auto expensiveBuilding = ai - > ah - > expensiveBuilding ( ) ;
//handling for early town development to save money and focus on income
if ( ! t - > hasBuilt ( ai - > ah - > getMaxPossibleGoldBuilding ( t ) ) & & expensiveBuilding . is_initialized ( ) )
{
auto potentialBuilding = expensiveBuilding . get ( ) ;
switch ( expensiveBuilding . get ( ) . bid )
{
case BuildingID : : TOWN_HALL :
case BuildingID : : CITY_HALL :
case BuildingID : : CAPITOL :
case BuildingID : : FORT :
case BuildingID : : CITADEL :
case BuildingID : : CASTLE :
//If above buildings are next to be bought, but no money... do not buy anything else, try to gather resources for these. Simple but has to suffice for now.
auto goal = ai - > ah - > whatToDo ( potentialBuilding . price , sptr ( Goals : : BuildThis ( potentialBuilding . bid , t ) . setpriority ( 2.25 ) ) ) ;
ret . push_back ( goal ) ;
return ret ;
break ;
}
}
if ( immediateBuilding . is_initialized ( ) )
2018-08-05 09:21:41 +02:00
{
2018-10-13 21:15:46 +02:00
ret . push_back ( sptr ( Goals : : BuildThis ( immediateBuilding . get ( ) . bid , t ) . setpriority ( 2 ) ) ) ; //prioritize buildings we can build quick
2018-08-05 09:21:41 +02:00
}
else //try build later
{
2018-10-13 21:15:46 +02:00
if ( expensiveBuilding . is_initialized ( ) )
2018-08-05 09:21:41 +02:00
{
2018-10-13 21:15:46 +02:00
auto potentialBuilding = expensiveBuilding . get ( ) ; //gather resources for any we can't afford
auto goal = ai - > ah - > whatToDo ( potentialBuilding . price , sptr ( Goals : : BuildThis ( potentialBuilding . bid , t ) . setpriority ( 0.5 ) ) ) ;
2018-08-05 09:21:41 +02:00
ret . push_back ( goal ) ;
}
}
}
if ( ret . empty ( ) )
2018-08-21 12:55:31 +02:00
throw cannotFulfillGoalException ( " BUILD has been realized as much as possible. " ) ;
2018-08-05 09:21:41 +02:00
else
return ret ;
}
2013-10-18 20:17:25 +00:00
TSubgoal Build : : whatToDoToAchieve ( )
{
2018-08-05 09:21:41 +02:00
return fh - > chooseSolution ( getAllPossibleSubgoals ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2018-07-26 12:06:55 +02:00
bool Goals : : Build : : fulfillsMe ( TSubgoal goal )
{
if ( goal - > goalType = = Goals : : BUILD | | goal - > goalType = = Goals : : BUILD_STRUCTURE )
return ( ! town | | town = = goal - > town ) ; //building anything will do, in this town if set
else
return false ;
}
2013-10-18 20:17:25 +00:00
TSubgoal Invalid : : whatToDoToAchieve ( )
{
2013-11-15 17:30:16 +00:00
return iAmElementar ( ) ;
2013-10-18 20:17:25 +00:00
}
2013-11-24 08:21:51 +00:00
std : : string GatherArmy : : completeMessage ( ) const
{
return " Hero " + hero . get ( ) - > name + " gathered army of value " + boost : : lexical_cast < std : : string > ( value ) ;
2014-01-16 20:24:06 +00:00
}
2013-11-24 08:21:51 +00:00
2013-10-18 20:17:25 +00:00
TSubgoal GatherArmy : : whatToDoToAchieve ( )
{
//TODO: find hero if none set
2013-12-23 20:46:01 +00:00
assert ( hero . h ) ;
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
return fh - > chooseSolution ( getAllPossibleSubgoals ( ) ) ; //find dwelling. use current hero to prevent him from doing nothing.
2013-12-23 20:46:01 +00:00
}
2015-12-04 01:06:02 +02:00
static const BuildingID unitsSource [ ] = { BuildingID : : DWELL_LVL_1 , BuildingID : : DWELL_LVL_2 , BuildingID : : DWELL_LVL_3 ,
BuildingID : : DWELL_LVL_4 , BuildingID : : DWELL_LVL_5 , BuildingID : : DWELL_LVL_6 , BuildingID : : DWELL_LVL_7 } ;
2013-12-23 20:46:01 +00:00
TGoalVec GatherArmy : : getAllPossibleSubgoals ( )
{
//get all possible towns, heroes and dwellings we may use
TGoalVec ret ;
2016-01-19 20:25:15 +03:00
2018-08-12 14:31:31 +03:00
if ( ! hero . validAndSet ( ) )
{
return ret ;
}
2013-12-23 20:46:01 +00:00
//TODO: include evaluation of monsters gather in calculation
2018-04-07 15:44:14 +07:00
for ( auto t : cb - > getTownsInfo ( ) )
2013-10-18 20:17:25 +00:00
{
2013-12-23 20:46:01 +00:00
auto pos = t - > visitablePos ( ) ;
2018-04-07 15:44:14 +07:00
if ( ai - > isAccessibleForHero ( pos , hero ) )
2013-10-18 20:17:25 +00:00
{
2018-07-26 12:06:55 +02:00
//grab army from town
2018-04-07 15:44:14 +07:00
if ( ! t - > visitingHero & & howManyReinforcementsCanGet ( hero , t ) )
2013-12-23 20:46:01 +00:00
{
2018-04-07 15:44:14 +07:00
if ( ! vstd : : contains ( ai - > townVisitsThisWeek [ hero ] , t ) )
ret . push_back ( sptr ( Goals : : VisitTile ( pos ) . sethero ( hero ) ) ) ;
2013-12-23 20:46:01 +00:00
}
2018-07-26 12:06:55 +02:00
//buy army in town
2018-09-11 13:09:14 +03:00
if ( ! t - > visitingHero | | t - > visitingHero = = hero . get ( true ) )
2018-07-26 12:06:55 +02:00
{
ui32 val = std : : min < ui32 > ( value , howManyReinforcementsCanBuy ( hero , t ) ) ;
if ( val )
2018-07-26 13:29:46 +02:00
{
auto goal = sptr ( Goals : : BuyArmy ( t , val ) . sethero ( hero ) ) ;
2018-09-11 13:09:14 +03:00
if ( ! ai - > ah - > containsObjective ( goal ) ) //avoid loops caused by reserving same objective twice
2018-07-26 13:29:46 +02:00
ret . push_back ( goal ) ;
2018-09-11 13:09:14 +03:00
else
logAi - > debug ( " Can not buy army, because of ai->ah->containsObjective " ) ;
2018-07-26 13:29:46 +02:00
}
2018-07-26 12:06:55 +02:00
}
//build dwelling
2018-08-21 12:55:31 +02:00
//TODO: plan building over multiple turns?
//auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
2018-10-12 20:33:15 +02:00
//Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
/*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
2018-07-29 18:27:21 +02:00
if ( bid . is_initialized ( ) )
2018-07-26 12:06:55 +02:00
{
2018-07-29 18:27:21 +02:00
auto goal = sptr ( BuildThis ( bid . get ( ) , t ) . setpriority ( priority ) ) ;
2018-09-02 18:04:38 +03:00
if ( ! ai - > ah - > containsObjective ( goal ) ) //avoid loops caused by reserving same objective twice
2018-07-26 13:29:46 +02:00
ret . push_back ( goal ) ;
2018-09-11 13:09:14 +03:00
else
logAi - > debug ( " Can not build a structure, because of ai->ah->containsObjective " ) ;
2018-10-12 20:33:15 +02:00
} */
2013-10-18 20:17:25 +00:00
}
}
2013-12-23 20:46:01 +00:00
auto otherHeroes = cb - > getHeroesInfo ( ) ;
auto heroDummy = hero ;
2015-12-29 02:14:08 +03:00
vstd : : erase_if ( otherHeroes , [ heroDummy ] ( const CGHeroInstance * h )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
if ( h = = heroDummy . h )
return true ;
else if ( ! ai - > isAccessibleForHero ( heroDummy - > visitablePos ( ) , h , true ) )
return true ;
2018-08-08 12:38:32 +02:00
else if ( ! ai - > canGetArmy ( heroDummy . h , h ) ) //TODO: return actual aiValue
2018-04-07 15:44:14 +07:00
return true ;
else if ( ai - > getGoal ( h ) - > goalType = = Goals : : GATHER_ARMY )
return true ;
else
return false ;
2013-12-23 20:46:01 +00:00
} ) ;
2018-04-07 15:44:14 +07:00
for ( auto h : otherHeroes )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
// Go to the other hero if we are faster
2018-08-12 14:31:31 +03:00
if ( ! vstd : : contains ( ai - > visitedHeroes [ hero ] , h )
& & ai - > isAccessibleForHero ( h - > visitablePos ( ) , hero , true ) ) //visit only once each turn //FIXME: this is only bug workaround
2018-08-05 09:21:41 +02:00
ret . push_back ( sptr ( Goals : : VisitHero ( h - > id . getNum ( ) ) . setisAbstract ( true ) . sethero ( hero ) ) ) ;
2018-04-07 15:44:14 +07:00
// Let the other hero come to us
2018-08-05 09:21:41 +02:00
if ( ! vstd : : contains ( ai - > visitedHeroes [ h ] , hero ) )
ret . push_back ( sptr ( Goals : : VisitHero ( hero - > id . getNum ( ) ) . setisAbstract ( true ) . sethero ( h ) ) ) ;
2013-12-23 20:46:01 +00:00
}
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
std : : vector < const CGObjectInstance * > objs ;
for ( auto obj : ai - > visitableObjs )
2013-12-23 20:46:01 +00:00
{
if ( obj - > ID = = Obj : : CREATURE_GENERATOR1 )
{
2013-10-18 20:17:25 +00:00
auto relationToOwner = cb - > getPlayerRelations ( obj - > getOwner ( ) , ai - > playerID ) ;
//Use flagged dwellings only when there are available creatures that we can afford
if ( relationToOwner = = PlayerRelations : : SAME_PLAYER )
{
2018-04-07 15:44:14 +07:00
auto dwelling = dynamic_cast < const CGDwelling * > ( obj ) ;
2018-09-11 13:09:14 +03:00
ui32 val = std : : min < ui32 > ( value , howManyReinforcementsCanBuy ( hero , dwelling ) ) ;
if ( val )
2013-10-18 20:17:25 +00:00
{
2018-09-11 13:09:14 +03:00
for ( auto & creLevel : dwelling - > creatures )
2013-10-18 20:17:25 +00:00
{
2018-09-11 13:09:14 +03:00
if ( creLevel . first )
2013-10-18 20:17:25 +00:00
{
2018-09-11 13:09:14 +03:00
for ( auto & creatureID : creLevel . second )
{
auto creature = VLC - > creh - > creatures [ creatureID ] ;
if ( ai - > ah - > freeResources ( ) . canAfford ( creature - > cost ) )
objs . push_back ( obj ) ; //TODO: reserve resources?
}
2013-10-18 20:17:25 +00:00
}
}
}
}
}
}
2013-12-23 20:46:01 +00:00
for ( auto h : cb - > getHeroesInfo ( ) )
{
2018-04-07 15:44:14 +07:00
for ( auto obj : objs )
{
//find safe dwelling
2018-08-12 14:31:31 +03:00
if ( ai - > isGoodForVisit ( obj , h ) )
{
vstd : : concatenate ( ret , ai - > ah - > howToVisitObj ( h , obj ) ) ;
}
2013-12-23 20:46:01 +00:00
}
}
2016-11-26 18:03:09 +01:00
2018-09-02 18:04:38 +03:00
if ( ai - > canRecruitAnyHero ( ) & & ai - > ah - > freeGold ( ) > GameConstants : : HERO_GOLD_COST ) //this is not stupid in early phase of game
2016-11-26 18:03:09 +01:00
{
2017-07-15 00:15:08 +02:00
if ( auto t = ai - > findTownWithTavern ( ) )
2016-11-26 18:03:09 +01:00
{
2018-04-07 15:44:14 +07:00
for ( auto h : cb - > getAvailableHeroes ( t ) ) //we assume that all towns have same set of heroes
2016-11-26 18:03:09 +01:00
{
2017-07-15 00:15:08 +02:00
if ( h & & h - > getTotalStrength ( ) > 500 ) //do not buy heroes with single creatures for GatherArmy
2016-11-26 18:03:09 +01:00
{
ret . push_back ( sptr ( Goals : : RecruitHero ( ) ) ) ;
break ;
}
}
}
}
2014-02-17 17:28:39 +00:00
2018-04-07 15:44:14 +07:00
if ( ret . empty ( ) )
2014-02-17 07:36:03 +00:00
{
2018-08-08 12:38:32 +02:00
if ( hero = = ai - > primaryHero ( ) )
2018-04-07 15:44:14 +07:00
ret . push_back ( sptr ( Goals : : Explore ( ) ) ) ;
2018-08-08 12:38:32 +02:00
else
throw cannotFulfillGoalException ( " No ways to gather army " ) ;
2014-02-17 07:36:03 +00:00
}
2013-10-18 20:17:25 +00:00
2013-12-23 20:46:01 +00:00
return ret ;
2013-10-18 20:17:25 +00:00
}
2013-11-23 18:16:25 +00:00
//TSubgoal AbstractGoal::whatToDoToAchieve()
//{
2016-08-13 17:44:37 +03:00
// logAi->debug("Decomposing goal of type %s",name());
2013-11-23 18:16:25 +00:00
// return sptr (Goals::Explore());
//}
2013-10-18 20:17:25 +00:00
2018-04-07 15:44:14 +07:00
TSubgoal AbstractGoal : : goVisitOrLookFor ( const CGObjectInstance * obj )
2013-10-18 20:17:25 +00:00
{
if ( obj )
2018-08-10 16:42:55 +02:00
return sptr ( Goals : : VisitObj ( obj - > id . getNum ( ) ) ) ;
2013-10-18 20:17:25 +00:00
else
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Explore ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2013-11-15 17:30:16 +00:00
TSubgoal AbstractGoal : : lookForArtSmart ( int aid )
2013-10-18 20:17:25 +00:00
{
2018-04-07 15:44:14 +07:00
return sptr ( Goals : : Invalid ( ) ) ;
2013-10-18 20:17:25 +00:00
}
2013-11-15 17:30:16 +00:00
bool AbstractGoal : : invalid ( ) const
2013-10-18 20:17:25 +00:00
{
return goalType = = INVALID ;
2013-10-20 13:51:05 +00:00
}
2013-11-23 18:16:25 +00:00
2018-04-07 15:44:14 +07:00
void AbstractGoal : : accept ( VCAI * ai )
2013-11-23 18:16:25 +00:00
{
ai - > tryRealize ( * this ) ;
2013-11-24 11:36:51 +00:00
}
2013-12-19 20:21:21 +00:00
2013-11-24 11:36:51 +00:00
template < typename T >
2018-04-07 15:44:14 +07:00
void CGoal < T > : : accept ( VCAI * ai )
2013-11-24 11:36:51 +00:00
{
2018-04-07 15:44:14 +07:00
ai - > tryRealize ( static_cast < T & > ( * this ) ) ; //casting enforces template instantiation
2013-11-24 11:36:51 +00:00
}
2013-11-24 15:30:17 +00:00
2018-04-07 15:44:14 +07:00
float AbstractGoal : : accept ( FuzzyHelper * f )
2013-12-19 20:21:21 +00:00
{
return f - > evaluate ( * this ) ;
}
template < typename T >
2018-04-07 15:44:14 +07:00
float CGoal < T > : : accept ( FuzzyHelper * f )
2013-12-19 20:21:21 +00:00
{
2018-04-07 15:44:14 +07:00
return f - > evaluate ( static_cast < T & > ( * this ) ) ; //casting enforces template instantiation
2013-12-19 20:21:21 +00:00
}