2018-07-29 18:27:21 +02:00
/*
* BuildingManager . 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
*
*/
# include "StdInc.h"
# include "BuildingManager.h"
# include "../../CCallback.h"
# include "../../lib/mapObjects/MapObjects.h"
bool BuildingManager : : tryBuildThisStructure ( const CGTownInstance * t , BuildingID building , unsigned int maxDays )
{
if ( maxDays = = 0 )
{
logAi - > warn ( " Request to build building %d in 0 days! " , building . toEnum ( ) ) ;
return false ;
}
if ( ! vstd : : contains ( t - > town - > buildings , building ) )
return false ; // no such building in town
if ( t - > hasBuilt ( building ) ) //Already built? Shouldn't happen in general
return true ;
const CBuilding * buildPtr = t - > town - > buildings . at ( building ) ;
auto toBuild = buildPtr - > requirements . getFulfillmentCandidates ( [ & ] ( const BuildingID & buildID )
{
return t - > hasBuilt ( buildID ) ;
} ) ;
toBuild . push_back ( building ) ;
for ( BuildingID buildID : toBuild )
{
EBuildingState : : EBuildingState canBuild = cb - > canBuildStructure ( t , buildID ) ;
if ( canBuild = = EBuildingState : : HAVE_CAPITAL | | canBuild = = EBuildingState : : FORBIDDEN | | canBuild = = EBuildingState : : NO_WATER )
return false ; //we won't be able to build this
}
if ( maxDays & & toBuild . size ( ) > maxDays )
return false ;
//TODO: calculate if we have enough resources to build it in maxDays?
for ( const auto & buildID : toBuild )
{
const CBuilding * b = t - > town - > buildings . at ( buildID ) ;
EBuildingState : : EBuildingState canBuild = cb - > canBuildStructure ( t , buildID ) ;
if ( canBuild = = EBuildingState : : ALLOWED )
{
PotentialBuilding pb ;
pb . bid = buildID ;
pb . price = t - > getBuildingCost ( buildID ) ;
immediateBuildings . push_back ( pb ) ; //these are checked again in try
return true ;
}
else if ( canBuild = = EBuildingState : : PREREQUIRES )
{
// can happen when dependencies have their own missing dependencies
if ( tryBuildThisStructure ( t , buildID , maxDays - 1 ) )
return true ;
}
else if ( canBuild = = EBuildingState : : MISSING_BASE )
{
if ( tryBuildThisStructure ( t , b - > upgrade , maxDays - 1 ) )
return true ;
}
else if ( canBuild = = EBuildingState : : NO_RESOURCES )
{
//we may need to gather resources for those
PotentialBuilding pb ;
pb . bid = buildID ;
pb . price = t - > getBuildingCost ( buildID ) ;
expensiveBuildings . push_back ( pb ) ; //these are checked again in try
return false ;
}
else
return false ;
}
return false ;
}
bool BuildingManager : : tryBuildAnyStructure ( const CGTownInstance * t , std : : vector < BuildingID > buildList , unsigned int maxDays )
{
for ( const auto & building : buildList )
{
if ( t - > hasBuilt ( building ) )
continue ;
return tryBuildThisStructure ( t , building , maxDays ) ;
}
return false ; //Can't build anything
}
boost : : optional < BuildingID > BuildingManager : : canBuildAnyStructure ( const CGTownInstance * t , const std : : vector < BuildingID > & buildList , unsigned int maxDays ) const
{
for ( const auto & building : buildList )
{
if ( t - > hasBuilt ( building ) )
continue ;
2018-08-21 12:55:31 +02:00
switch ( cb - > canBuildStructure ( t , building ) )
{
case EBuildingState : : ALLOWED :
case EBuildingState : : NO_RESOURCES : //TODO: allow this via optional parameter?
return boost : : optional < BuildingID > ( building ) ;
break ;
}
2018-07-29 18:27:21 +02:00
}
return boost : : optional < BuildingID > ( ) ; //Can't build anything
}
bool BuildingManager : : tryBuildNextStructure ( const CGTownInstance * t , std : : vector < BuildingID > buildList , unsigned int maxDays )
{
for ( const auto & building : buildList )
{
if ( t - > hasBuilt ( building ) )
continue ;
return tryBuildThisStructure ( t , building , maxDays ) ;
}
return false ; //Nothing to build
}
2018-10-09 21:31:44 +02:00
void BuildingManager : : init ( CPlayerSpecificInfoCallback * CB )
2018-07-29 18:27:21 +02:00
{
cb = CB ;
}
void BuildingManager : : setAI ( VCAI * AI )
{
ai = AI ;
}
//Set of buildings for different goals. Does not include any prerequisites.
2018-10-16 19:03:25 +02:00
static const std : : vector < BuildingID > essential = { BuildingID : : TAVERN , BuildingID : : TOWN_HALL } ;
static const std : : vector < BuildingID > basicGoldSource = { BuildingID : : TOWN_HALL , BuildingID : : CITY_HALL } ;
static const std : : vector < BuildingID > capitolAndRequirements = { BuildingID : : FORT , BuildingID : : CITADEL , BuildingID : : CASTLE , BuildingID : : CAPITOL } ;
static const std : : vector < BuildingID > unitsSource = { BuildingID : : DWELL_LVL_1 , BuildingID : : DWELL_LVL_2 , BuildingID : : DWELL_LVL_3 ,
2018-07-29 18:27:21 +02:00
BuildingID : : DWELL_LVL_4 , BuildingID : : DWELL_LVL_5 , BuildingID : : DWELL_LVL_6 , BuildingID : : DWELL_LVL_7 } ;
2018-10-16 19:03:25 +02:00
static const std : : vector < BuildingID > unitsUpgrade = { BuildingID : : DWELL_LVL_1_UP , BuildingID : : DWELL_LVL_2_UP , BuildingID : : DWELL_LVL_3_UP ,
2018-07-29 18:27:21 +02:00
BuildingID : : DWELL_LVL_4_UP , BuildingID : : DWELL_LVL_5_UP , BuildingID : : DWELL_LVL_6_UP , BuildingID : : DWELL_LVL_7_UP } ;
2018-10-16 19:03:25 +02:00
static const std : : vector < BuildingID > unitGrowth = { BuildingID : : FORT , BuildingID : : CITADEL , BuildingID : : CASTLE , BuildingID : : HORDE_1 ,
2018-07-29 18:27:21 +02:00
BuildingID : : HORDE_1_UPGR , BuildingID : : HORDE_2 , BuildingID : : HORDE_2_UPGR } ;
2018-10-16 19:03:25 +02:00
static const std : : vector < BuildingID > _spells = { BuildingID : : MAGES_GUILD_1 , BuildingID : : MAGES_GUILD_2 , BuildingID : : MAGES_GUILD_3 ,
2018-07-29 18:27:21 +02:00
BuildingID : : MAGES_GUILD_4 , BuildingID : : MAGES_GUILD_5 } ;
2018-10-16 19:03:25 +02:00
static const std : : vector < BuildingID > extra = { BuildingID : : RESOURCE_SILO , BuildingID : : SPECIAL_1 , BuildingID : : SPECIAL_2 , BuildingID : : SPECIAL_3 ,
2018-07-29 18:27:21 +02:00
BuildingID : : SPECIAL_4 , BuildingID : : SHIPYARD } ; // all remaining buildings
bool BuildingManager : : getBuildingOptions ( const CGTownInstance * t )
{
//TODO make *real* town development system
//TODO: faction-specific development: use special buildings, build dwellings in better order, etc
//TODO: build resource silo, defences when needed
//Possible - allow "locking" on specific building (build prerequisites and then building itself)
2018-10-12 20:33:15 +02:00
//TODO: There is some disabled building code in GatherTroops and GatherArmy - take it into account when enhancing building. For now AI works best with building only via Build goal.
2018-07-29 18:27:21 +02:00
immediateBuildings . clear ( ) ;
expensiveBuildings . clear ( ) ;
2018-10-13 21:15:46 +02:00
//below algorithm focuses on economy growth at start of the game, saving money instead of build rushing is handled by Build goal
//changing code blocks order will alter behavior by changing order of adding elements to immediateBuildings / expensiveBuildings
2018-07-29 18:27:21 +02:00
2023-03-17 01:19:04 +02:00
// TResources currentRes = cb->getResourceAmount();
// TResources currentIncome = t->dailyIncome();
2018-07-29 18:27:21 +02:00
2018-10-16 19:03:25 +02:00
if ( tryBuildAnyStructure ( t , essential ) )
2018-07-29 18:27:21 +02:00
return true ;
2018-10-12 20:33:15 +02:00
//the more gold the better and less problems later //TODO: what about building mage guild / marketplace etc. with city hall disabled in editor?
2018-10-16 19:03:25 +02:00
if ( tryBuildNextStructure ( t , basicGoldSource ) )
2018-10-12 20:33:15 +02:00
return true ;
2018-10-13 18:52:44 +02:00
//workaround for mantis #2696 - build capitol with separate algorithm if it is available
if ( vstd : : contains ( t - > builtBuildings , BuildingID : : CITY_HALL ) & & getMaxPossibleGoldBuilding ( t ) = = BuildingID : : CAPITOL )
{
2018-10-16 19:03:25 +02:00
if ( tryBuildNextStructure ( t , capitolAndRequirements ) )
2018-10-13 18:52:44 +02:00
return true ;
}
2018-10-12 20:33:15 +02:00
if ( ! t - > hasBuilt ( BuildingID : : FORT ) ) //in vast majority of situations fort is top priority building if we already have city hall, TODO: unite with unitGrowth building chain
if ( tryBuildThisStructure ( t , BuildingID : : FORT ) )
return true ;
2018-07-29 18:27:21 +02:00
if ( cb - > getDate ( Date : : DAY_OF_WEEK ) > 6 ) // last 2 days of week - try to focus on growth
{
2018-10-16 19:03:25 +02:00
if ( tryBuildNextStructure ( t , unitGrowth , 2 ) )
2018-07-29 18:27:21 +02:00
return true ;
}
2018-10-13 18:52:44 +02:00
//try building dwellings
if ( t - > hasBuilt ( BuildingID : : FORT ) )
2018-07-29 18:27:21 +02:00
{
2018-10-16 19:03:25 +02:00
if ( tryBuildAnyStructure ( t , unitsSource , 8 - cb - > getDate ( Date : : DAY_OF_WEEK ) ) )
2018-07-29 18:27:21 +02:00
return true ;
}
//try to upgrade dwelling
2018-10-16 19:03:25 +02:00
for ( int i = 0 ; i < unitsUpgrade . size ( ) ; i + + )
2018-07-29 18:27:21 +02:00
{
2018-10-09 20:23:36 +02:00
if ( t - > hasBuilt ( unitsSource [ i ] ) & & ! t - > hasBuilt ( unitsUpgrade [ i ] ) & & t - > hasBuilt ( BuildingID : : FORT ) )
2018-07-29 18:27:21 +02:00
{
if ( tryBuildThisStructure ( t , unitsUpgrade [ i ] ) )
return true ;
}
}
//remaining tasks
2018-10-16 19:03:25 +02:00
if ( tryBuildNextStructure ( t , _spells ) )
2018-07-29 18:27:21 +02:00
return true ;
2018-10-16 19:03:25 +02:00
if ( tryBuildAnyStructure ( t , extra ) )
2018-07-29 18:27:21 +02:00
return true ;
//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
std : : vector < BuildingID > extraBuildings ;
for ( auto buildingInfo : t - > town - > buildings )
{
if ( buildingInfo . first > 43 )
extraBuildings . push_back ( buildingInfo . first ) ;
}
2019-03-20 21:58:15 +02:00
return tryBuildAnyStructure ( t , extraBuildings ) ;
2018-07-29 18:27:21 +02:00
}
2018-10-13 18:52:44 +02:00
BuildingID BuildingManager : : getMaxPossibleGoldBuilding ( const CGTownInstance * t )
{
if ( cb - > canBuildStructure ( t , BuildingID : : CAPITOL ) ! = EBuildingState : : HAVE_CAPITAL & & cb - > canBuildStructure ( t , BuildingID : : CAPITOL ) ! = EBuildingState : : FORBIDDEN )
return BuildingID : : CAPITOL ;
else if ( cb - > canBuildStructure ( t , BuildingID : : CITY_HALL ) ! = EBuildingState : : FORBIDDEN )
return BuildingID : : CITY_HALL ;
else if ( cb - > canBuildStructure ( t , BuildingID : : TOWN_HALL ) ! = EBuildingState : : FORBIDDEN )
return BuildingID : : TOWN_HALL ;
else
return BuildingID : : VILLAGE_HALL ;
}
2018-07-29 18:27:21 +02:00
boost : : optional < PotentialBuilding > BuildingManager : : immediateBuilding ( ) const
{
if ( immediateBuildings . size ( ) )
return boost : : optional < PotentialBuilding > ( immediateBuildings . front ( ) ) ; //back? whatever
else
return boost : : optional < PotentialBuilding > ( ) ;
}
boost : : optional < PotentialBuilding > BuildingManager : : expensiveBuilding ( ) const
{
if ( expensiveBuildings . size ( ) )
return boost : : optional < PotentialBuilding > ( expensiveBuildings . front ( ) ) ;
else
return boost : : optional < PotentialBuilding > ( ) ;
}