1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00
vcmi/AI/VCAI/Goals/CollectRes.cpp

213 lines
5.3 KiB
C++
Raw Normal View History

2018-12-01 10:30:37 +02:00
/*
* CollectRes.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 "Goals.h"
#include "../VCAI.h"
#include "../AIUtility.h"
#include "../AIhelper.h"
#include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
2023-05-24 01:05:59 +02:00
#include "../../../lib/mapObjects/CGMarket.h"
2018-12-01 10:30:37 +02:00
#include "../../../lib/StringConstants.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
bool CollectRes::operator==(const CollectRes & other) const
{
return resID == other.resID;
}
TGoalVec 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:
2023-04-05 02:26:29 +02:00
return resID == GameResID(EGameResID::GOLD);
2018-12-01 10:30:37 +02:00
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:
2023-04-05 02:26:29 +02:00
switch (GameResID(resID).toEnum())
2018-12-01 10:30:37 +02:00
{
2023-04-05 02:26:29 +02:00
case EGameResID::GOLD:
case EGameResID::WOOD:
2018-12-01 10:30:37 +02:00
return false;
}
break;
case Obj::WATER_WHEEL:
2023-04-05 02:26:29 +02:00
if (resID != GameResID(EGameResID::GOLD))
2018-12-01 10:30:37 +02:00
return false;
break;
case Obj::MYSTICAL_GARDEN:
2023-04-05 02:26:29 +02:00
if ((resID != GameResID(EGameResID::GOLD)) && (resID != GameResID(EGameResID::GEMS)))
2018-12-01 10:30:37 +02:00
return false;
break;
case Obj::LEAN_TO:
case Obj::WAGON:
2023-04-05 02:26:29 +02:00
if (resID != GameResID(EGameResID::GOLD))
2018-12-01 10:30:37 +02:00
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);
}
for (auto obj : ourObjs)
{
auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
vstd::concatenate(ret, waysToGo);
}
}
return ret;
}
TSubgoal CollectRes::whatToDoToAchieve()
{
auto goals = getAllPossibleSubgoals();
auto trade = whatToDoToTrade();
if (!trade->invalid())
goals.push_back(trade);
if (goals.empty())
return sptr(Explore()); //we can always do that
else
return fh->chooseSolution(goals); //TODO: evaluate trading
}
TSubgoal CollectRes::whatToDoToTrade()
{
std::vector<const IMarket *> markets;
std::vector<const CGObjectInstance *> visObjs;
ai->retrieveVisitableObjs(visObjs, true);
2023-05-01 13:29:56 +02:00
for(const CGObjectInstance * obj : visObjs)
2018-12-01 10:30:37 +02:00
{
2023-06-15 13:22:33 +02:00
if(const IMarket * m = IMarket::castFrom(obj, false); m && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
2018-12-01 10:30:37 +02:00
{
2023-05-01 13:29:56 +02:00
if(obj->ID == Obj::TOWN)
{
if(obj->tempOwner == ai->playerID)
markets.push_back(m);
}
else
2018-12-01 10:30:37 +02:00
markets.push_back(m);
}
}
boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
{
return m1->getMarketEfficiency() < m2->getMarketEfficiency();
});
markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
{
auto * o = dynamic_cast<const CGObjectInstance *>(market);
if(o && !(o->ID == Obj::TOWN && o->tempOwner == ai->playerID))
2018-12-01 10:30:37 +02:00
{
if(!ai->isAccessible(o->visitablePos()))
2018-12-01 10:30:37 +02:00
return true;
}
return false;
}), markets.end());
if (!markets.size())
{
for (const CGTownInstance * t : cb->getTownsInfo())
{
if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
}
}
else
{
const IMarket * m = markets.back();
//attempt trade at back (best prices)
int howManyCanWeBuy = 0;
for (GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i)
2018-12-01 10:30:37 +02:00
{
2023-04-05 02:26:29 +02:00
if (GameResID(i) == resID)
2018-12-01 10:30:37 +02:00
continue;
int toGive = -1, toReceive = -1;
2023-04-05 02:26:29 +02:00
m->getOffer(GameResID(i), resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
2018-12-01 10:30:37 +02:00
assert(toGive > 0 && toReceive > 0);
howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
}
if (howManyCanWeBuy >= value)
{
auto * o = dynamic_cast<const CGObjectInstance *>(m);
auto backObj = cb->getTopObj(o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
2018-12-01 10:30:37 +02:00
assert(backObj);
auto objid = o->id.getNum();
2018-12-01 10:30:37 +02:00
if (backObj->tempOwner != ai->playerID) //top object not owned
{
return sptr(VisitObj(objid)); //just go there
}
else //either it's our town, or we have hero there
{
2023-04-05 02:26:29 +02:00
return sptr(Trade(static_cast<EGameResID>(resID), value, objid).setisElementar(true)); //we can do this immediately
2018-12-01 10:30:37 +02:00
}
}
}
return sptr(Invalid()); //cannot trade
}
bool CollectRes::fulfillsMe(TSubgoal goal)
{
if (goal->resID == resID)
if (goal->value >= value)
return true;
return false;
}