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"
2024-07-21 10:49:40 +00:00
#include "../../lib/entities/building/CBuilding.h"
2018-07-29 18:27:21 +02:00
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 )
{
2023-08-19 22:35:44 +03:00
EBuildingState canBuild = cb -> canBuildStructure ( t , buildID );
2018-07-29 18:27:21 +02:00
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 );
2023-08-19 22:35:44 +03:00
EBuildingState canBuild = cb -> canBuildStructure ( t , buildID );
2018-07-29 18:27:21 +02:00
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
}
2023-04-16 20:42:56 +03:00
std :: optional < BuildingID > BuildingManager :: canBuildAnyStructure ( const CGTownInstance * t , const std :: vector < BuildingID > & buildList , unsigned int maxDays ) const
2018-07-29 18:27:21 +02:00
{
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?
2023-04-16 20:42:56 +03:00
return std :: optional < BuildingID > ( building );
2018-08-21 12:55:31 +02:00
break ;
}
2018-07-29 18:27:21 +02:00
}
2023-04-16 20:42:56 +03:00
return std :: optional < BuildingID > (); //Can't build anything
2018-07-29 18:27:21 +02:00
}
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 22:31:44 +03: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 };
2024-01-25 03:26:42 +02:00
static const std :: vector < BuildingID > defence = { BuildingID :: FORT , BuildingID :: CITADEL , BuildingID :: CASTLE };
2018-10-16 19:03:25 +02:00
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 };
2024-01-25 03:26:42 +02:00
static const std :: vector < BuildingID > unitGrowth = { BuildingID :: HORDE_1 , 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 };
2024-02-12 13:49:45 +02:00
static const std :: vector < BuildingID > extra = { BuildingID :: MARKETPLACE , BuildingID :: BLACKSMITH , BuildingID :: RESOURCE_SILO , BuildingID :: SPECIAL_1 , BuildingID :: SPECIAL_2 ,
2024-01-26 00:56:36 +02:00
BuildingID :: SPECIAL_3 , BuildingID :: SPECIAL_4 , BuildingID :: SHIPYARD }; // all remaining buildings
2018-07-29 18:27:21 +02:00
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 02:19:04 +03: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 ;
2024-01-25 03:26:42 +02:00
if ( cb -> getDate ( Date :: DAY_OF_WEEK ) < 5 ) // first 4 days of week - try to focus on dwellings
{
if ( tryBuildNextStructure ( t , unitsSource , 4 ))
return true ;
}
if ( cb -> getDate ( Date :: DAY_OF_WEEK ) > 4 ) // last 3 days of week - try to focus on growth by building Fort/Citadel/Castle
{
if ( tryBuildNextStructure ( t , defence , 3 ))
return true ;
}
if ( t -> hasBuilt ( BuildingID :: CASTLE ))
{
2024-01-26 00:56:36 +02:00
if ( tryBuildAnyStructure ( t , unitGrowth ))
2024-01-25 03:26:42 +02:00
return true ;
}
2024-01-26 00:56:36 +02:00
//try to make City Hall
if ( tryBuildNextStructure ( t , basicGoldSource ))
2018-07-29 18:27:21 +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-07-29 18:27:21 +02:00
//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 )
{
2023-08-20 00:22:31 +03:00
if ( buildingInfo . first > BuildingID :: DWELL_UP2_FIRST )
2018-07-29 18:27:21 +02:00
extraBuildings . push_back ( buildingInfo . first );
}
2019-03-20 20:58:15 +01: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 ;
}
2023-04-16 20:42:56 +03:00
std :: optional < PotentialBuilding > BuildingManager :: immediateBuilding () const
2018-07-29 18:27:21 +02:00
{
if ( immediateBuildings . size ())
2023-04-16 20:42:56 +03:00
return std :: optional < PotentialBuilding > ( immediateBuildings . front ()); //back? whatever
2018-07-29 18:27:21 +02:00
else
2023-04-16 20:42:56 +03:00
return std :: optional < PotentialBuilding > ();
2018-07-29 18:27:21 +02:00
}
2023-04-16 20:42:56 +03:00
std :: optional < PotentialBuilding > BuildingManager :: expensiveBuilding () const
2018-07-29 18:27:21 +02:00
{
if ( expensiveBuildings . size ())
2023-04-16 20:42:56 +03:00
return std :: optional < PotentialBuilding > ( expensiveBuildings . front ());
2018-07-29 18:27:21 +02:00
else
2023-04-16 20:42:56 +03:00
return std :: optional < PotentialBuilding > ();
2018-07-29 18:27:21 +02:00
}