mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Nullkiller: initial cleanup of unused code
This commit is contained in:
parent
5344df51c6
commit
af0dcf97c4
@ -13,8 +13,6 @@
|
||||
|
||||
AIhelper::AIhelper()
|
||||
{
|
||||
resourceManager.reset(new ResourceManager());
|
||||
buildingManager.reset(new BuildingManager());
|
||||
pathfindingManager.reset(new PathfindingManager());
|
||||
armyManager.reset(new ArmyManager());
|
||||
heroManager.reset(new HeroManager());
|
||||
@ -24,15 +22,8 @@ AIhelper::~AIhelper()
|
||||
{
|
||||
}
|
||||
|
||||
bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal)
|
||||
{
|
||||
return resourceManager->notifyGoalCompleted(goal);
|
||||
}
|
||||
|
||||
void AIhelper::init(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
resourceManager->init(CB);
|
||||
buildingManager->init(CB);
|
||||
pathfindingManager->init(CB);
|
||||
armyManager->init(CB);
|
||||
heroManager->init(CB);
|
||||
@ -40,8 +31,6 @@ void AIhelper::init(CPlayerSpecificInfoCallback * CB)
|
||||
|
||||
void AIhelper::setAI(VCAI * AI)
|
||||
{
|
||||
resourceManager->setAI(AI);
|
||||
buildingManager->setAI(AI);
|
||||
pathfindingManager->setAI(AI);
|
||||
armyManager->setAI(AI);
|
||||
heroManager->setAI(AI);
|
||||
@ -53,106 +42,6 @@ void AIhelper::update()
|
||||
heroManager->update();
|
||||
}
|
||||
|
||||
bool AIhelper::getBuildingOptions(const CGTownInstance * t)
|
||||
{
|
||||
return buildingManager->getBuildingOptions(t);
|
||||
}
|
||||
|
||||
BuildingID AIhelper::getMaxPossibleGoldBuilding(const CGTownInstance * t)
|
||||
{
|
||||
return buildingManager->getMaxPossibleGoldBuilding(t);
|
||||
}
|
||||
|
||||
boost::optional<PotentialBuilding> AIhelper::immediateBuilding() const
|
||||
{
|
||||
return buildingManager->immediateBuilding();
|
||||
}
|
||||
|
||||
boost::optional<PotentialBuilding> AIhelper::expensiveBuilding() const
|
||||
{
|
||||
return buildingManager->expensiveBuilding();
|
||||
}
|
||||
|
||||
boost::optional<BuildingID> AIhelper::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
|
||||
{
|
||||
return buildingManager->canBuildAnyStructure(t, buildList, maxDays);
|
||||
}
|
||||
|
||||
Goals::TSubgoal AIhelper::whatToDo(TResources & res, Goals::TSubgoal goal)
|
||||
{
|
||||
return resourceManager->whatToDo(res, goal);
|
||||
}
|
||||
|
||||
Goals::TSubgoal AIhelper::whatToDo() const
|
||||
{
|
||||
return resourceManager->whatToDo();
|
||||
}
|
||||
|
||||
bool AIhelper::containsObjective(Goals::TSubgoal goal) const
|
||||
{
|
||||
return resourceManager->containsObjective(goal);
|
||||
}
|
||||
|
||||
bool AIhelper::hasTasksLeft() const
|
||||
{
|
||||
return resourceManager->hasTasksLeft();
|
||||
}
|
||||
|
||||
bool AIhelper::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal&)> predicate)
|
||||
{
|
||||
return resourceManager->removeOutdatedObjectives(predicate);
|
||||
}
|
||||
|
||||
bool AIhelper::canAfford(const TResources & cost) const
|
||||
{
|
||||
return resourceManager->canAfford(cost);
|
||||
}
|
||||
|
||||
TResources AIhelper::reservedResources() const
|
||||
{
|
||||
return resourceManager->reservedResources();
|
||||
}
|
||||
|
||||
TResources AIhelper::freeResources() const
|
||||
{
|
||||
return resourceManager->freeResources();
|
||||
}
|
||||
|
||||
TResource AIhelper::freeGold() const
|
||||
{
|
||||
return resourceManager->freeGold();
|
||||
}
|
||||
|
||||
TResources AIhelper::allResources() const
|
||||
{
|
||||
return resourceManager->allResources();
|
||||
}
|
||||
|
||||
TResource AIhelper::allGold() const
|
||||
{
|
||||
return resourceManager->allGold();
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitTile(const int3 & tile, bool allowGatherArmy) const
|
||||
{
|
||||
return pathfindingManager->howToVisitTile(tile, allowGatherArmy);
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitObj(ObjectIdRef obj, bool allowGatherArmy) const
|
||||
{
|
||||
return pathfindingManager->howToVisitObj(obj, allowGatherArmy);
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
|
||||
{
|
||||
return pathfindingManager->howToVisitTile(hero, tile, allowGatherArmy);
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
|
||||
{
|
||||
return pathfindingManager->howToVisitObj(hero, obj, allowGatherArmy);
|
||||
}
|
||||
|
||||
std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
return pathfindingManager->getPathsToTile(hero, tile);
|
||||
|
@ -14,8 +14,6 @@
|
||||
!!! Note: Include THIS file at the end of include list to avoid "undefined base class" error
|
||||
*/
|
||||
|
||||
#include "ResourceManager.h"
|
||||
#include "BuildingManager.h"
|
||||
#include "ArmyManager.h"
|
||||
#include "HeroManager.h"
|
||||
#include "Pathfinding/PathfindingManager.h"
|
||||
@ -24,14 +22,11 @@ class ResourceManager;
|
||||
class BuildingManager;
|
||||
|
||||
|
||||
//indirection interface for various modules
|
||||
class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager, public IArmyManager, public IHeroManager
|
||||
//TODO: remove class, put managers to engine
|
||||
class DLL_EXPORT AIhelper : public IPathfindingManager, public IArmyManager, public IHeroManager
|
||||
{
|
||||
friend class VCAI;
|
||||
friend struct SetGlobalState; //mess?
|
||||
|
||||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<BuildingManager> buildingManager;
|
||||
//std::shared_ptr<ResourceManager> resourceManager;
|
||||
//std::shared_ptr<BuildingManager> buildingManager;
|
||||
std::shared_ptr<PathfindingManager> pathfindingManager;
|
||||
std::shared_ptr<ArmyManager> armyManager;
|
||||
std::shared_ptr<HeroManager> heroManager;
|
||||
@ -40,29 +35,6 @@ public:
|
||||
AIhelper();
|
||||
~AIhelper();
|
||||
|
||||
bool canAfford(const TResources & cost) const;
|
||||
TResources reservedResources() const override;
|
||||
TResources freeResources() const override;
|
||||
TResource freeGold() const override;
|
||||
TResources allResources() const override;
|
||||
TResource allGold() const override;
|
||||
|
||||
Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) override;
|
||||
Goals::TSubgoal whatToDo() const override;
|
||||
bool containsObjective(Goals::TSubgoal goal) const override;
|
||||
bool hasTasksLeft() const override;
|
||||
bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
|
||||
|
||||
bool getBuildingOptions(const CGTownInstance * t) override;
|
||||
BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
|
||||
boost::optional<PotentialBuilding> immediateBuilding() const override;
|
||||
boost::optional<PotentialBuilding> expensiveBuilding() const override;
|
||||
boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
|
||||
|
||||
Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
|
||||
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||
@ -95,9 +67,6 @@ public:
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
|
||||
|
||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
};
|
||||
|
@ -91,6 +91,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
|
||||
continue;
|
||||
|
||||
if(path.specialAction && !path.specialAction->canAct(path.targetHero))
|
||||
{
|
||||
auto subGoal = path.specialAction->whatToDo(path.targetHero);
|
||||
}
|
||||
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||
|
||||
#ifdef AI_TRACE_LEVEL >= 2
|
||||
|
@ -148,7 +148,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
if(treatIsUnderControl)
|
||||
continue;
|
||||
|
||||
if(cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST)
|
||||
if(!town->visitingHero
|
||||
&& town->hasBuilt(BuildingID::TAVERN)
|
||||
&& cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST)
|
||||
{
|
||||
auto heroesInTavern = cb->getAvailableHeroes(town);
|
||||
|
||||
|
@ -56,6 +56,8 @@ Goals::TGoalVec GatherArmyBehavior::getTasks()
|
||||
{
|
||||
vstd::concatenate(tasks, upgradeArmy(town));
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * hero) const
|
||||
|
@ -1,257 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
switch (cb->canBuildStructure(t, building))
|
||||
{
|
||||
case EBuildingState::ALLOWED:
|
||||
case EBuildingState::NO_RESOURCES: //TODO: allow this via optional parameter?
|
||||
return boost::optional<BuildingID>(building);
|
||||
break;
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
void BuildingManager::init(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
cb = CB;
|
||||
}
|
||||
|
||||
void BuildingManager::setAI(VCAI * AI)
|
||||
{
|
||||
ai = AI;
|
||||
}
|
||||
//Set of buildings for different goals. Does not include any prerequisites.
|
||||
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,
|
||||
BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 };
|
||||
static const std::vector<BuildingID> unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
|
||||
BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP };
|
||||
static const std::vector<BuildingID> unitGrowth = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
|
||||
BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR };
|
||||
static const std::vector<BuildingID> _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
|
||||
BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 };
|
||||
static const std::vector<BuildingID> extra = { BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
|
||||
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)
|
||||
|
||||
//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.
|
||||
|
||||
immediateBuildings.clear();
|
||||
expensiveBuildings.clear();
|
||||
|
||||
//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
|
||||
|
||||
TResources currentRes = cb->getResourceAmount();
|
||||
TResources currentIncome = t->dailyIncome();
|
||||
|
||||
if(tryBuildAnyStructure(t, essential))
|
||||
return true;
|
||||
|
||||
//the more gold the better and less problems later //TODO: what about building mage guild / marketplace etc. with city hall disabled in editor?
|
||||
if(tryBuildNextStructure(t, basicGoldSource))
|
||||
return true;
|
||||
|
||||
//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)
|
||||
{
|
||||
if(tryBuildNextStructure(t, capitolAndRequirements))
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
if (cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth
|
||||
{
|
||||
if (tryBuildNextStructure(t, unitGrowth, 2))
|
||||
return true;
|
||||
}
|
||||
|
||||
//try building dwellings
|
||||
if (t->hasBuilt(BuildingID::FORT))
|
||||
{
|
||||
if (tryBuildAnyStructure(t, unitsSource, 8 - cb->getDate(Date::DAY_OF_WEEK)))
|
||||
return true;
|
||||
}
|
||||
|
||||
//try to upgrade dwelling
|
||||
for (int i = 0; i < unitsUpgrade.size(); i++)
|
||||
{
|
||||
if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]) && t->hasBuilt(BuildingID::FORT))
|
||||
{
|
||||
if (tryBuildThisStructure(t, unitsUpgrade[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//remaining tasks
|
||||
if (tryBuildNextStructure(t, _spells))
|
||||
return true;
|
||||
if (tryBuildAnyStructure(t, extra))
|
||||
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);
|
||||
}
|
||||
return tryBuildAnyStructure(t, extraBuildings);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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>();
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* BuildingManager.h, 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AIUtility.h"
|
||||
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "VCAI.h"
|
||||
|
||||
struct DLL_EXPORT PotentialBuilding
|
||||
{
|
||||
BuildingID bid;
|
||||
TResources price;
|
||||
//days to build?
|
||||
};
|
||||
|
||||
class DLL_EXPORT IBuildingManager //: public: IAbstractManager
|
||||
{ //info about town development
|
||||
public:
|
||||
virtual ~IBuildingManager() = default;
|
||||
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
||||
virtual void setAI(VCAI * AI) = 0;
|
||||
|
||||
virtual bool getBuildingOptions(const CGTownInstance * t) = 0;
|
||||
virtual boost::optional<PotentialBuilding> immediateBuilding() const = 0;
|
||||
virtual boost::optional<PotentialBuilding> expensiveBuilding() const = 0;
|
||||
virtual boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT BuildingManager : public IBuildingManager
|
||||
{
|
||||
friend class VCAI;
|
||||
friend class AIhelper;
|
||||
friend struct SetGlobalState;
|
||||
|
||||
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
|
||||
VCAI * ai;
|
||||
|
||||
public:
|
||||
|
||||
//try build anything in given town, and execute resulting Goal if any
|
||||
bool getBuildingOptions(const CGTownInstance * t) override;
|
||||
BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
|
||||
boost::optional<PotentialBuilding> immediateBuilding() const override;
|
||||
boost::optional<PotentialBuilding> expensiveBuilding() const override;
|
||||
boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
|
||||
|
||||
protected:
|
||||
|
||||
bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
|
||||
//try build first unbuilt structure
|
||||
bool tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7);
|
||||
//try build ANY unbuilt structure
|
||||
|
||||
bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
|
||||
|
||||
private:
|
||||
//TODO: remember current town?
|
||||
std::vector<PotentialBuilding> immediateBuildings; //what we can build right now in current town
|
||||
std::vector<PotentialBuilding> expensiveBuildings; //what we coudl build but can't afford
|
||||
|
||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
};
|
@ -17,19 +17,12 @@ set(VCAI_SRCS
|
||||
AIhelper.cpp
|
||||
ArmyManager.cpp
|
||||
HeroManager.cpp
|
||||
ResourceManager.cpp
|
||||
BuildingManager.cpp
|
||||
SectorMap.cpp
|
||||
BuildingManager.cpp
|
||||
MapObjectsEvaluator.cpp
|
||||
FuzzyEngines.cpp
|
||||
FuzzyHelper.cpp
|
||||
Goals/AbstractGoal.cpp
|
||||
Goals/BuildBoat.cpp
|
||||
Goals/Build.cpp
|
||||
Goals/BuildThis.cpp
|
||||
Goals/Explore.cpp
|
||||
Goals/GatherArmy.cpp
|
||||
Goals/DismissHero.cpp
|
||||
Goals/GatherTroops.cpp
|
||||
Goals/BuyArmy.cpp
|
||||
@ -41,8 +34,6 @@ set(VCAI_SRCS
|
||||
Goals/CollectRes.cpp
|
||||
Goals/Trade.cpp
|
||||
Goals/RecruitHero.cpp
|
||||
Goals/Conquer.cpp
|
||||
Goals/ClearWayTo.cpp
|
||||
Goals/DigAtTile.cpp
|
||||
Goals/GetArtOfType.cpp
|
||||
Goals/FindObj.cpp
|
||||
@ -85,10 +76,6 @@ set(VCAI_HEADERS
|
||||
AIhelper.h
|
||||
ArmyManager.h
|
||||
HeroManager.h
|
||||
ResourceManager.h
|
||||
BuildingManager.h
|
||||
SectorMap.h
|
||||
BuildingManager.h
|
||||
MapObjectsEvaluator.h
|
||||
FuzzyEngines.h
|
||||
FuzzyHelper.h
|
||||
@ -96,10 +83,7 @@ set(VCAI_HEADERS
|
||||
Goals/CGoal.h
|
||||
Goals/Invalid.h
|
||||
Goals/BuildBoat.h
|
||||
Goals/Build.h
|
||||
Goals/BuildThis.h
|
||||
Goals/Explore.h
|
||||
Goals/GatherArmy.h
|
||||
Goals/DismissHero.h
|
||||
Goals/GatherTroops.h
|
||||
Goals/BuyArmy.h
|
||||
@ -111,8 +95,6 @@ set(VCAI_HEADERS
|
||||
Goals/CollectRes.h
|
||||
Goals/Trade.h
|
||||
Goals/RecruitHero.h
|
||||
Goals/Conquer.h
|
||||
Goals/ClearWayTo.h
|
||||
Goals/DigAtTile.h
|
||||
Goals/GetArtOfType.h
|
||||
Goals/FindObj.h
|
||||
|
@ -184,7 +184,7 @@ void Nullkiller::makeTurn()
|
||||
}
|
||||
catch(goalFulfilledException &)
|
||||
{
|
||||
logAi->trace(bestTask->completeMessage());
|
||||
logAi->trace("Task %s completed", bestTask->name());
|
||||
}
|
||||
catch(std::exception & e)
|
||||
{
|
||||
|
@ -23,7 +23,8 @@ extern FuzzyHelper * fh;
|
||||
|
||||
engineBase::engineBase()
|
||||
{
|
||||
engine.addRuleBlock(&rules);
|
||||
rules = new fl::RuleBlock();
|
||||
engine.addRuleBlock(rules);
|
||||
}
|
||||
|
||||
void engineBase::configure()
|
||||
@ -34,7 +35,7 @@ void engineBase::configure()
|
||||
|
||||
void engineBase::addRule(const std::string & txt)
|
||||
{
|
||||
rules.addRule(fl::Rule::parse(txt, &engine));
|
||||
rules->addRule(fl::Rule::parse(txt, &engine));
|
||||
}
|
||||
|
||||
struct armyStructure
|
||||
|
@ -17,7 +17,7 @@ class engineBase //subclasses create fuzzylite variables with "new" that are not
|
||||
{
|
||||
protected:
|
||||
fl::Engine engine;
|
||||
fl::RuleBlock rules;
|
||||
fl::RuleBlock * rules;
|
||||
virtual void configure();
|
||||
void addRule(const std::string & txt);
|
||||
public:
|
||||
|
@ -19,44 +19,6 @@ FuzzyHelper * fh;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
|
||||
Goals::TSubgoal FuzzyHelper::chooseSolution(Goals::TGoalVec vec)
|
||||
{
|
||||
if(vec.empty())
|
||||
{
|
||||
logAi->debug("FuzzyHelper found no goals. Returning Goals::Invalid.");
|
||||
|
||||
//no possibilities found
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
//a trick to switch between heroes less often - calculatePaths is costly
|
||||
auto sortByHeroes = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
|
||||
{
|
||||
return lhs->hero.h < rhs->hero.h;
|
||||
};
|
||||
boost::sort(vec, sortByHeroes);
|
||||
|
||||
for(auto g : vec)
|
||||
{
|
||||
setPriority(g);
|
||||
}
|
||||
|
||||
auto compareGoals = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
|
||||
{
|
||||
return lhs->priority < rhs->priority;
|
||||
};
|
||||
|
||||
for(auto goal : vec)
|
||||
{
|
||||
logAi->trace("FuzzyHelper evaluated goal %s, priority=%.4f", goal->name(), goal->priority);
|
||||
}
|
||||
|
||||
Goals::TSubgoal result = *boost::max_element(vec, compareGoals);
|
||||
|
||||
logAi->debug("FuzzyHelper returned goal %s, priority=%.4f", result->name(), result->priority);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
|
||||
{
|
||||
@ -77,134 +39,6 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
|
||||
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::VisitTile & g)
|
||||
{
|
||||
if(g.parent)
|
||||
{
|
||||
g.parent->accept(this);
|
||||
}
|
||||
|
||||
return visitTileEngine.evaluate(g);
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::BuildBoat & g)
|
||||
{
|
||||
const float buildBoatPenalty = 0.25;
|
||||
|
||||
if(!g.parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g.parent->accept(this) - buildBoatPenalty;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::AdventureSpellCast & g)
|
||||
{
|
||||
if(!g.parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const CSpell * spell = g.getSpell();
|
||||
const float spellCastPenalty = (float)g.hero->getSpellCost(spell) / g.hero->mana;
|
||||
|
||||
return g.parent->accept(this) - spellCastPenalty;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::CompleteQuest & g)
|
||||
{
|
||||
// TODO: How to evaluate quest complexity?
|
||||
const float questPenalty = 0.2;
|
||||
|
||||
if(!g.parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g.parent->accept(this) * questPenalty;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::VisitObj & g)
|
||||
{
|
||||
if(g.parent)
|
||||
{
|
||||
g.parent->accept(this);
|
||||
}
|
||||
|
||||
return visitObjEngine.evaluate(g);
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::VisitHero & g)
|
||||
{
|
||||
auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
|
||||
if(!obj)
|
||||
return -100; //hero died in the meantime
|
||||
else
|
||||
{
|
||||
g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).accept(this));
|
||||
}
|
||||
return g.priority;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::GatherArmy & g)
|
||||
{
|
||||
//the more army we need, the more important goal
|
||||
//the more army we lack, the less important goal
|
||||
float army = g.hero->getArmyStrength();
|
||||
float ratio = g.value / std::max(g.value - army, 2000.0f); //2000 is about the value of hero recruited from tavern
|
||||
return 5 * (ratio / (ratio + 2)); //so 50% army gives 2.5, asymptotic 5
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::ClearWayTo & g)
|
||||
{
|
||||
if (!g.hero.h)
|
||||
return 0; //lowest priority
|
||||
|
||||
return g.whatToDoToAchieve()->accept(this);
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::BuildThis & g)
|
||||
{
|
||||
return g.priority; //TODO
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::DigAtTile & g)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::CollectRes & g)
|
||||
{
|
||||
return g.priority; //handled by ResourceManager
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::Build & g)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::BuyArmy & g)
|
||||
{
|
||||
return g.priority;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::Explore & g)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::RecruitHero & g)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::Invalid & g)
|
||||
{
|
||||
return -1e10;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::AbstractGoal & g)
|
||||
{
|
||||
logAi->warn("Cannot evaluate goal %s", g.name());
|
||||
return g.priority;
|
||||
}
|
||||
void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pattern
|
||||
{
|
||||
g->setpriority(g->accept(this)); //this enforces returned value is set
|
||||
}
|
||||
|
||||
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
|
||||
{
|
||||
return evaluateDanger(tile, visitor, ai.get());
|
||||
|
@ -16,33 +16,9 @@ class DLL_EXPORT FuzzyHelper
|
||||
{
|
||||
public:
|
||||
TacticalAdvantageEngine tacticalAdvantageEngine;
|
||||
VisitTileEngine visitTileEngine;
|
||||
VisitObjEngine visitObjEngine;
|
||||
|
||||
float evaluate(Goals::Explore & g);
|
||||
float evaluate(Goals::RecruitHero & g);
|
||||
float evaluate(Goals::VisitTile & g);
|
||||
float evaluate(Goals::VisitObj & g);
|
||||
float evaluate(Goals::VisitHero & g);
|
||||
float evaluate(Goals::BuildThis & g);
|
||||
float evaluate(Goals::DigAtTile & g);
|
||||
float evaluate(Goals::CollectRes & g);
|
||||
float evaluate(Goals::Build & g);
|
||||
float evaluate(Goals::BuyArmy & g);
|
||||
float evaluate(Goals::BuildBoat & g);
|
||||
float evaluate(Goals::GatherArmy & g);
|
||||
float evaluate(Goals::ClearWayTo & g);
|
||||
float evaluate(Goals::CompleteQuest & g);
|
||||
float evaluate(Goals::AdventureSpellCast & g);
|
||||
float evaluate(Goals::Invalid & g);
|
||||
float evaluate(Goals::AbstractGoal & g);
|
||||
void setPriority(Goals::TSubgoal & g);
|
||||
|
||||
ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
|
||||
|
||||
Goals::TSubgoal chooseSolution(Goals::TGoalVec vec);
|
||||
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
|
||||
|
||||
ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
|
||||
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards = true);
|
||||
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
|
||||
|
@ -12,8 +12,6 @@
|
||||
#include "../VCAI.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -115,63 +113,12 @@ bool AbstractGoal::operator==(const AbstractGoal & g) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool 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;
|
||||
}
|
||||
|
||||
//TODO: find out why the following are not generated automatically on MVS?
|
||||
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
|
||||
}
|
||||
|
||||
bool TSubgoal::operator<(const TSubgoal & rhs) const
|
||||
{
|
||||
return get() < rhs.get(); //compae by value
|
||||
}
|
||||
|
||||
bool AbstractGoal::invalid() const
|
||||
{
|
||||
return goalType == EGoals::INVALID;
|
||||
@ -182,11 +129,6 @@ void AbstractGoal::accept(VCAI * ai)
|
||||
ai->tryRealize(*this);
|
||||
}
|
||||
|
||||
float AbstractGoal::accept(FuzzyHelper * f)
|
||||
{
|
||||
return f->evaluate(*this);
|
||||
}
|
||||
|
||||
EvaluationContext::EvaluationContext()
|
||||
: movementCost(0.0),
|
||||
manaCost(0),
|
||||
|
@ -30,7 +30,6 @@ namespace Goals
|
||||
class BuildThis;
|
||||
class DigAtTile;
|
||||
class CollectRes;
|
||||
class Build;
|
||||
class BuyArmy;
|
||||
class BuildBoat;
|
||||
class GatherArmy;
|
||||
@ -43,7 +42,8 @@ namespace Goals
|
||||
enum EGoals
|
||||
{
|
||||
INVALID = -1,
|
||||
WIN, CONQUER, BUILD, //build needs to get a real reasoning
|
||||
WIN, CONQUER,
|
||||
BUILD,
|
||||
EXPLORE, GATHER_ARMY,
|
||||
BOOST_HERO,
|
||||
RECRUIT_HERO,
|
||||
@ -162,24 +162,14 @@ namespace Goals
|
||||
EGoals goalType;
|
||||
|
||||
virtual std::string name() const;
|
||||
virtual std::string completeMessage() const
|
||||
{
|
||||
return "This goal is unspecified!";
|
||||
}
|
||||
|
||||
bool invalid() const;
|
||||
|
||||
///Visitor pattern
|
||||
//TODO: make accept work for std::shared_ptr... somehow
|
||||
virtual void accept(VCAI * ai); //unhandled goal will report standard error
|
||||
virtual float accept(FuzzyHelper * f);
|
||||
|
||||
virtual bool operator==(const AbstractGoal & g) const;
|
||||
bool operator<(AbstractGoal & g); //final
|
||||
virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
|
||||
{
|
||||
return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
|
||||
}
|
||||
|
||||
bool operator!=(const AbstractGoal & g) const
|
||||
{
|
||||
|
@ -77,8 +77,3 @@ std::string AdventureSpellCast::name() const
|
||||
{
|
||||
return "AdventureSpellCast " + spellID.toSpell()->name;
|
||||
}
|
||||
|
||||
std::string AdventureSpellCast::completeMessage() const
|
||||
{
|
||||
return "Spell casted successfully " + spellID.toSpell()->name;
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const AdventureSpellCast & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -26,23 +26,23 @@ bool BuildBoat::operator==(const BuildBoat & other) const
|
||||
return shipyard->o->id == other.shipyard->o->id;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
//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)
|
||||
{
|
||||
@ -79,8 +79,3 @@ std::string BuildBoat::name() const
|
||||
{
|
||||
return "BuildBoat";
|
||||
}
|
||||
|
||||
std::string BuildBoat::completeMessage() const
|
||||
{
|
||||
return "Boat have been built at " + shipyard->o->visitablePos().toString();
|
||||
}
|
||||
|
@ -28,10 +28,8 @@ namespace Goals
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const BuildBoat & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -35,45 +33,3 @@ std::string BuildThis::name() const
|
||||
{
|
||||
return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name;
|
||||
}
|
||||
|
||||
TSubgoal BuildThis::whatToDoToAchieve()
|
||||
{
|
||||
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
|
||||
{
|
||||
switch(cb->canBuildStructure(town, b))
|
||||
{
|
||||
case EBuildingState::ALLOWED:
|
||||
case EBuildingState::NO_RESOURCES:
|
||||
{
|
||||
auto res = town->town->buildings.at(BuildingID(bid))->resources;
|
||||
return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw cannotFulfillGoalException("Not possible to build");
|
||||
}
|
||||
}
|
||||
else
|
||||
throw cannotFulfillGoalException("Cannot find town to build this");
|
||||
}
|
||||
|
@ -47,12 +47,6 @@ namespace Goals
|
||||
bid = Bid;
|
||||
priority = 1;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const BuildThis & other) const override;
|
||||
virtual std::string name() const override;
|
||||
};
|
||||
|
@ -25,21 +25,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const
|
||||
return town == other.town && objid == other.objid;
|
||||
}
|
||||
|
||||
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
|
||||
return ai->ah->whatToDo(price, iAmElementar()); //buy right now or gather resources
|
||||
}
|
||||
|
||||
std::string BuyArmy::completeMessage() const
|
||||
{
|
||||
return boost::format("Bought army of value %d in town of %s") % value, town->name;
|
||||
return iAmElementar();
|
||||
}
|
@ -32,10 +32,8 @@ namespace Goals
|
||||
value = val; //expressed in AI unit strength
|
||||
priority = 3;//TODO: evaluate?
|
||||
}
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const BuyArmy & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -51,11 +51,6 @@ namespace Goals
|
||||
ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
|
||||
float accept(FuzzyHelper * f) override
|
||||
{
|
||||
return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
|
||||
CGoal<T> * clone() const override
|
||||
{
|
||||
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -35,80 +33,80 @@ 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
|
||||
//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
|
||||
};
|
||||
// //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
|
||||
//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 : 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));
|
||||
// for (auto obj : ourObjs)
|
||||
// {
|
||||
// auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
|
||||
|
||||
vstd::concatenate(ret, waysToGo);
|
||||
}
|
||||
}
|
||||
// vstd::concatenate(ret, waysToGo);
|
||||
// }
|
||||
//}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -119,90 +117,78 @@ TSubgoal CollectRes::whatToDoToAchieve()
|
||||
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
|
||||
return sptr(Invalid()); //we can always do that
|
||||
}
|
||||
|
||||
TSubgoal CollectRes::whatToDoToTrade()
|
||||
{
|
||||
std::vector<const IMarket *> markets;
|
||||
//std::vector<const IMarket *> markets;
|
||||
|
||||
std::vector<const CGObjectInstance *> visObjs;
|
||||
ai->retrieveVisitableObjs(visObjs, true);
|
||||
for (const CGObjectInstance * obj : visObjs)
|
||||
{
|
||||
if (const IMarket * m = IMarket::castFrom(obj, false))
|
||||
{
|
||||
if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
|
||||
markets.push_back(m);
|
||||
else if (obj->ID == Obj::TRADING_POST)
|
||||
markets.push_back(m);
|
||||
}
|
||||
}
|
||||
//std::vector<const CGObjectInstance *> visObjs;
|
||||
//ai->retrieveVisitableObjs(visObjs, true);
|
||||
//for (const CGObjectInstance * obj : visObjs)
|
||||
//{
|
||||
// if (const IMarket * m = IMarket::castFrom(obj, false))
|
||||
// {
|
||||
// if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
|
||||
// markets.push_back(m);
|
||||
// else if (obj->ID == Obj::TRADING_POST)
|
||||
// markets.push_back(m);
|
||||
// }
|
||||
//}
|
||||
|
||||
boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
|
||||
{
|
||||
return m1->getMarketEfficiency() < m2->getMarketEfficiency();
|
||||
});
|
||||
//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
|
||||
{
|
||||
if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
|
||||
{
|
||||
if (!ai->isAccessible(market->o->visitablePos()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}), markets.end());
|
||||
//markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
|
||||
//{
|
||||
// if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
|
||||
// {
|
||||
// if (!ai->isAccessible(market->o->visitablePos()))
|
||||
// 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 (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
||||
{
|
||||
if (i == resID)
|
||||
continue;
|
||||
int toGive = -1, toReceive = -1;
|
||||
m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
|
||||
assert(toGive > 0 && toReceive > 0);
|
||||
howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
|
||||
}
|
||||
//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 (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
||||
// {
|
||||
// if (i == resID)
|
||||
// continue;
|
||||
// int toGive = -1, toReceive = -1;
|
||||
// m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
|
||||
// assert(toGive > 0 && toReceive > 0);
|
||||
// howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
|
||||
// }
|
||||
|
||||
if (howManyCanWeBuy >= value)
|
||||
{
|
||||
auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
||||
assert(backObj);
|
||||
auto objid = m->o->id.getNum();
|
||||
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
|
||||
{
|
||||
return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (howManyCanWeBuy >= value)
|
||||
// {
|
||||
// auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
||||
// assert(backObj);
|
||||
// auto objid = m->o->id.getNum();
|
||||
// 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
|
||||
// {
|
||||
// return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
return sptr(Invalid()); //cannot trade
|
||||
}
|
||||
|
||||
bool CollectRes::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->resID == resID)
|
||||
if (goal->value >= value)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ namespace Goals
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
TSubgoal whatToDoToTrade();
|
||||
bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
|
||||
virtual bool operator==(const CollectRes & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -82,9 +82,8 @@ TSubgoal CompleteQuest::whatToDoToAchieve()
|
||||
|
||||
TGoalVec solutions = getAllPossibleSubgoals();
|
||||
|
||||
if(solutions.empty())
|
||||
throw cannotFulfillGoalException("Can not complete quest " + questToString());
|
||||
|
||||
/*
|
||||
TSubgoal result = fh->chooseSolution(solutions);
|
||||
|
||||
logAi->trace(
|
||||
@ -94,7 +93,7 @@ TSubgoal CompleteQuest::whatToDoToAchieve()
|
||||
result->objid,
|
||||
result->hero.validAndSet() ? result->hero->name : "not specified");
|
||||
|
||||
return result;
|
||||
return result;*/
|
||||
}
|
||||
|
||||
std::string CompleteQuest::name() const
|
||||
@ -102,11 +101,6 @@ std::string CompleteQuest::name() const
|
||||
return "CompleteQuest";
|
||||
}
|
||||
|
||||
std::string CompleteQuest::completeMessage() const
|
||||
{
|
||||
return "Completed quest " + questToString();
|
||||
}
|
||||
|
||||
std::string CompleteQuest::questToString() const
|
||||
{
|
||||
if(q.quest->missionType == CQuest::MISSION_NONE)
|
||||
@ -128,7 +122,7 @@ TGoalVec CompleteQuest::tryCompleteQuest() const
|
||||
{
|
||||
if(q.quest->checkQuest(hero))
|
||||
{
|
||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id)));
|
||||
//vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +222,7 @@ TGoalVec CompleteQuest::missionResources() const
|
||||
{
|
||||
if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is
|
||||
{
|
||||
return ai->ah->howToVisitObj(q.obj);
|
||||
return solutions;// ai->ah->howToVisitObj(q.obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -254,7 +248,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const
|
||||
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
|
||||
|
||||
if(!obj)
|
||||
return ai->ah->howToVisitObj(q.obj);
|
||||
return solutions;// ai->ah->howToVisitObj(q.obj);
|
||||
|
||||
if(obj->ID == Obj::HERO)
|
||||
{
|
||||
@ -264,11 +258,11 @@ TGoalVec CompleteQuest::missionDestroyObj() const
|
||||
{
|
||||
auto heroToProtect = cb->getHero(obj->id);
|
||||
|
||||
solutions.push_back(sptr(GatherArmy().sethero(heroToProtect)));
|
||||
//solutions.push_back(sptr(GatherArmy().sethero(heroToProtect)));
|
||||
}
|
||||
else if(relations == PlayerRelations::ENEMIES)
|
||||
{
|
||||
solutions = ai->ah->howToVisitObj(obj);
|
||||
//solutions = ai->ah->howToVisitObj(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,6 @@ namespace Goals
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const CompleteQuest & other) const override;
|
||||
|
||||
private:
|
||||
|
@ -48,8 +48,3 @@ std::string DismissHero::name() const
|
||||
{
|
||||
return "DismissHero " + hero.name;
|
||||
}
|
||||
|
||||
std::string DismissHero::completeMessage() const
|
||||
{
|
||||
return "Hero dismissed successfully " + hero.name;
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const DismissHero & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -36,11 +36,6 @@ std::string ExchangeSwapTownHeroes::name() const
|
||||
return "Exchange and swap heroes of " + town->name;
|
||||
}
|
||||
|
||||
std::string ExchangeSwapTownHeroes::completeMessage() const
|
||||
{
|
||||
return "Exchange and swap heroes of " + town->name + " compelete";
|
||||
}
|
||||
|
||||
bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
|
||||
{
|
||||
return town == other.town;
|
||||
|
@ -35,7 +35,6 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -72,27 +72,29 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
{
|
||||
auto & node = chainPath.nodes[i];
|
||||
|
||||
HeroPtr hero = node.targetHero;
|
||||
const CGHeroInstance * hero = node.targetHero;
|
||||
HeroPtr heroPtr = hero;
|
||||
|
||||
if(vstd::contains(blockedIndexes, i))
|
||||
{
|
||||
blockedIndexes.insert(node.parentIndex);
|
||||
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN);
|
||||
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero.name, node.coord.toString());
|
||||
logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero->name, node.coord.toString());
|
||||
|
||||
try
|
||||
{
|
||||
if(hero->movement)
|
||||
{
|
||||
ai->nullkiller->setActive(hero.get(), node.coord);
|
||||
|
||||
ai->nullkiller->setActive(hero, node.coord);
|
||||
|
||||
if(node.specialAction)
|
||||
{
|
||||
if(node.specialAction->canAct(hero.get()))
|
||||
if(node.specialAction->canAct(hero))
|
||||
{
|
||||
auto specialGoal = node.specialAction->whatToDo(hero);
|
||||
|
||||
@ -100,12 +102,12 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: decompose
|
||||
throw cannotFulfillGoalException("Path is nondeterministic.");
|
||||
}
|
||||
|
||||
if(!hero.validAndSet())
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", hero.name);
|
||||
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -113,7 +115,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
|
||||
if(node.turns == 0 && node.coord != hero->visitablePos())
|
||||
{
|
||||
auto targetNode = cb->getPathsInfo(hero.get())->getPathInfo(node.coord);
|
||||
auto targetNode = cb->getPathsInfo(hero)->getPathInfo(node.coord);
|
||||
|
||||
if(targetNode->accessible == CGPathNode::EAccessibility::NOT_SET
|
||||
|| targetNode->accessible == CGPathNode::EAccessibility::BLOCKED
|
||||
@ -122,7 +124,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
{
|
||||
logAi->error(
|
||||
"Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this",
|
||||
hero.name,
|
||||
hero->name,
|
||||
node.coord.toString());
|
||||
|
||||
return;
|
||||
@ -137,9 +139,9 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
}
|
||||
catch(cannotFulfillGoalException)
|
||||
{
|
||||
if(!hero.validAndSet())
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", hero.name);
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -147,13 +149,13 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
if(hero->movement > 0)
|
||||
{
|
||||
CGPath path;
|
||||
bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord);
|
||||
bool isOk = cb->getPathsInfo(hero)->getPath(path, node.coord);
|
||||
|
||||
if(isOk && path.nodes.back().turns > 0)
|
||||
{
|
||||
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString());
|
||||
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->name, hero->movement, node.coord.toString());
|
||||
|
||||
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN);
|
||||
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -170,7 +172,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
{
|
||||
logAi->error(
|
||||
"Enable to complete chain. Expected hero %s to arive to %s but he is at %s",
|
||||
hero.name,
|
||||
hero->name,
|
||||
node.coord.toString(),
|
||||
hero->visitablePos().toString());
|
||||
|
||||
@ -178,14 +180,14 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
}
|
||||
|
||||
// no exception means we were not able to rich the tile
|
||||
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN);
|
||||
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
|
||||
blockedIndexes.insert(node.parentIndex);
|
||||
}
|
||||
catch(goalFulfilledException)
|
||||
{
|
||||
if(!hero)
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->debug("Hero %s was killed while attempting to rich %s", hero.name, node.coord.toString());
|
||||
logAi->debug("Hero %s was killed while attempting to rich %s", heroPtr.name, node.coord.toString());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -197,8 +199,3 @@ std::string ExecuteHeroChain::name() const
|
||||
{
|
||||
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name;
|
||||
}
|
||||
|
||||
std::string ExecuteHeroChain::completeMessage() const
|
||||
{
|
||||
return "Hero chain completed";
|
||||
}
|
@ -30,7 +30,6 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
||||
const AIPath & getPath() const { return chainPath; }
|
||||
};
|
||||
|
@ -233,14 +233,9 @@ bool Explore::operator==(const Explore & other) const
|
||||
return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy;
|
||||
}
|
||||
|
||||
std::string Explore::completeMessage() const
|
||||
{
|
||||
return "Hero " + hero.get()->name + " completed exploration";
|
||||
}
|
||||
|
||||
TSubgoal Explore::whatToDoToAchieve()
|
||||
{
|
||||
return fh->chooseSolution(getAllPossibleSubgoals());
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
TGoalVec Explore::getAllPossibleSubgoals()
|
||||
@ -361,18 +356,6 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Explore::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if(goal->goalType == EXPLORE)
|
||||
{
|
||||
if(goal->hero)
|
||||
return hero == goal->hero;
|
||||
else
|
||||
return true; //cancel ALL exploration
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const
|
||||
{
|
||||
ExplorationHelper scanResult(h, allowGatherArmy);
|
||||
|
@ -1,66 +1,64 @@
|
||||
/*
|
||||
* Explore.h, 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
struct ExplorationHelper;
|
||||
|
||||
class DLL_EXPORT Explore : public CGoal<Explore>
|
||||
{
|
||||
private:
|
||||
bool allowGatherArmy;
|
||||
|
||||
public:
|
||||
Explore(bool allowGatherArmy)
|
||||
: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
|
||||
{
|
||||
priority = 1;
|
||||
}
|
||||
|
||||
Explore()
|
||||
: Explore(true)
|
||||
{
|
||||
}
|
||||
|
||||
Explore(HeroPtr h)
|
||||
: CGoal(Goals::EXPLORE)
|
||||
{
|
||||
hero = h;
|
||||
priority = 1;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const Explore & other) const override;
|
||||
|
||||
private:
|
||||
TSubgoal exploreNearestNeighbour(HeroPtr h) const;
|
||||
TSubgoal explorationNewPoint(HeroPtr h) const;
|
||||
TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const;
|
||||
void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const;
|
||||
bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const;
|
||||
|
||||
void getVisibleNeighbours(
|
||||
const std::vector<int3> & tiles,
|
||||
std::vector<int3> & out,
|
||||
CCallback * cbp,
|
||||
const TeamState * ts) const;
|
||||
|
||||
int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const;
|
||||
};
|
||||
}
|
||||
///*
|
||||
//* Explore.h, 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
|
||||
//*
|
||||
//*/
|
||||
//#pragma once
|
||||
//
|
||||
//#include "CGoal.h"
|
||||
//
|
||||
//struct HeroPtr;
|
||||
//class VCAI;
|
||||
//class FuzzyHelper;
|
||||
//
|
||||
//namespace Goals
|
||||
//{
|
||||
// struct ExplorationHelper;
|
||||
//
|
||||
// class DLL_EXPORT Explore : public CGoal<Explore>
|
||||
// {
|
||||
// private:
|
||||
// bool allowGatherArmy;
|
||||
//
|
||||
// public:
|
||||
// Explore(bool allowGatherArmy)
|
||||
// : CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
|
||||
// {
|
||||
// priority = 1;
|
||||
// }
|
||||
//
|
||||
// Explore()
|
||||
// : Explore(true)
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// Explore(HeroPtr h)
|
||||
// : CGoal(Goals::EXPLORE)
|
||||
// {
|
||||
// hero = h;
|
||||
// priority = 1;
|
||||
// }
|
||||
// TGoalVec getAllPossibleSubgoals() override;
|
||||
// TSubgoal whatToDoToAchieve() override;
|
||||
// virtual bool operator==(const Explore & other) const override;
|
||||
//
|
||||
// private:
|
||||
// TSubgoal exploreNearestNeighbour(HeroPtr h) const;
|
||||
// TSubgoal explorationNewPoint(HeroPtr h) const;
|
||||
// TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const;
|
||||
// void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const;
|
||||
// bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const;
|
||||
//
|
||||
// void getVisibleNeighbours(
|
||||
// const std::vector<int3> & tiles,
|
||||
// std::vector<int3> & out,
|
||||
// CCallback * cbp,
|
||||
// const TeamState * ts) const;
|
||||
//
|
||||
// int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const;
|
||||
// };
|
||||
//}
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "FindObj.h"
|
||||
#include "VisitObj.h"
|
||||
#include "Explore.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
@ -52,19 +51,4 @@ TSubgoal FindObj::whatToDoToAchieve()
|
||||
}
|
||||
if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
|
||||
return sptr(VisitObj(o->id.getNum()));
|
||||
else
|
||||
return sptr(Explore());
|
||||
}
|
||||
|
||||
bool FindObj::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == 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;
|
||||
}
|
@ -41,7 +41,6 @@ namespace Goals
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const FindObj & other) const override;
|
||||
};
|
||||
}
|
@ -13,8 +13,6 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -62,12 +60,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
}
|
||||
}
|
||||
|
||||
TGoalVec solutions = getAllPossibleSubgoals();
|
||||
|
||||
if(solutions.empty())
|
||||
return sptr(Explore());
|
||||
|
||||
return fh->chooseSolution(solutions);
|
||||
return sptr(Invalid());
|
||||
}
|
||||
|
||||
|
||||
@ -75,75 +68,74 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec solutions;
|
||||
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
int count = getCreaturesCount(t->getUpperArmy());
|
||||
//for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
//{
|
||||
// int count = getCreaturesCount(t->getUpperArmy());
|
||||
|
||||
if(count >= this->value)
|
||||
{
|
||||
if(t->visitingHero)
|
||||
{
|
||||
solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get())));
|
||||
}
|
||||
else
|
||||
{
|
||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
|
||||
}
|
||||
// if(count >= this->value)
|
||||
// {
|
||||
// if(t->visitingHero)
|
||||
// {
|
||||
// solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get())));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
|
||||
// }
|
||||
|
||||
continue;
|
||||
}
|
||||
// continue;
|
||||
// }
|
||||
|
||||
auto creature = VLC->creh->creatures[objid];
|
||||
if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
|
||||
{
|
||||
auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
|
||||
if(!creatures)
|
||||
continue;
|
||||
// auto creature = VLC->creh->creatures[objid];
|
||||
// if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
|
||||
// {
|
||||
// auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
|
||||
// if(!creatures)
|
||||
// continue;
|
||||
|
||||
int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
|
||||
if(upgradeNumber < 0)
|
||||
continue;
|
||||
// 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);
|
||||
if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->cost)) //this assumes only creatures with dwellings are assigned to faction
|
||||
{
|
||||
solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid)));
|
||||
}
|
||||
/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
|
||||
{
|
||||
return sptr(BuildThis(bid, t).setpriority(priority));
|
||||
}*/
|
||||
}
|
||||
}
|
||||
for(auto obj : ai->visitableObjs)
|
||||
{
|
||||
auto d = dynamic_cast<const CGDwelling *>(obj);
|
||||
// BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
|
||||
// if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->cost)) //this assumes only creatures with dwellings are assigned to faction
|
||||
// {
|
||||
// solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid)));
|
||||
// }
|
||||
// /*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
|
||||
// {
|
||||
// return sptr(BuildThis(bid, t).setpriority(priority));
|
||||
// }*/
|
||||
// }
|
||||
//}
|
||||
|
||||
if(!d || obj->ID == Obj::TOWN)
|
||||
continue;
|
||||
//for(auto obj : ai->visitableObjs)
|
||||
//{
|
||||
// auto d = dynamic_cast<const CGDwelling *>(obj);
|
||||
|
||||
for(auto creature : d->creatures)
|
||||
{
|
||||
if(creature.first) //there are more than 0 creatures avaliabe
|
||||
{
|
||||
for(auto type : creature.second)
|
||||
{
|
||||
if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
|
||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if(!d || obj->ID == Obj::TOWN)
|
||||
// continue;
|
||||
|
||||
// for(auto creature : d->creatures)
|
||||
// {
|
||||
// if(creature.first) //there are more than 0 creatures avaliabe
|
||||
// {
|
||||
// for(auto type : creature.second)
|
||||
// {
|
||||
// if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
|
||||
// vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//CreatureID creID = CreatureID(objid);
|
||||
|
||||
//vstd::erase_if(solutions, [&](TSubgoal goal)->bool
|
||||
//{
|
||||
// return goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot();
|
||||
//});
|
||||
|
||||
return solutions;
|
||||
//TODO: exchange troops between heroes
|
||||
}
|
||||
|
||||
bool 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;
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ namespace Goals
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const GatherTroops & other) const override;
|
||||
|
||||
private:
|
||||
|
@ -12,22 +12,17 @@
|
||||
#include "CGoal.h"
|
||||
#include "Invalid.h"
|
||||
#include "BuildBoat.h"
|
||||
#include "Build.h"
|
||||
#include "BuildThis.h"
|
||||
#include "Conquer.h"
|
||||
#include "GatherArmy.h"
|
||||
#include "Win.h"
|
||||
#include "VisitObj.h"
|
||||
#include "VisitTile.h"
|
||||
#include "VisitHero.h"
|
||||
#include "Explore.h"
|
||||
#include "BuyArmy.h"
|
||||
#include "GatherTroops.h"
|
||||
#include "Trade.h"
|
||||
#include "CollectRes.h"
|
||||
#include "RecruitHero.h"
|
||||
#include "GetArtOfType.h"
|
||||
#include "ClearWayTo.h"
|
||||
#include "DigAtTile.h"
|
||||
#include "FindObj.h"
|
||||
#include "CompleteQuest.h"
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -25,14 +23,3 @@ extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
TSubgoal RecruitHero::whatToDoToAchieve()
|
||||
{
|
||||
const CGTownInstance * t = ai->findTownWithTavern();
|
||||
if(!t)
|
||||
return sptr(BuildThis(BuildingID::TAVERN).setpriority(2));
|
||||
|
||||
TResources res;
|
||||
res[Res::GOLD] = GameConstants::HERO_GOLD_COST;
|
||||
return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
|
||||
}
|
||||
|
@ -26,13 +26,6 @@ namespace Goals
|
||||
priority = 1;
|
||||
}
|
||||
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
|
||||
virtual bool operator==(const RecruitHero & other) const override
|
||||
{
|
||||
return true;
|
||||
|
@ -9,14 +9,11 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "VisitHero.h"
|
||||
#include "Explore.h"
|
||||
#include "Invalid.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
|
||||
|
||||
@ -30,45 +27,3 @@ bool VisitHero::operator==(const VisitHero & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.objid == objid;
|
||||
}
|
||||
|
||||
std::string VisitHero::completeMessage() const
|
||||
{
|
||||
return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
|
||||
}
|
||||
|
||||
TSubgoal VisitHero::whatToDoToAchieve()
|
||||
{
|
||||
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
|
||||
if(!obj)
|
||||
return sptr(Explore());
|
||||
int3 pos = obj->visitablePos();
|
||||
|
||||
if(hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
|
||||
{
|
||||
if(hero->visitablePos() == pos)
|
||||
logAi->error("Hero %s tries to visit himself.", hero.name);
|
||||
else
|
||||
{
|
||||
//can't use VISIT_TILE here as tile appears blocked by target hero
|
||||
//FIXME: elementar goal should not be abstract
|
||||
return sptr(VisitHero(objid).sethero(hero).settile(pos).setisElementar(true));
|
||||
}
|
||||
}
|
||||
return sptr(Invalid());
|
||||
}
|
||||
|
||||
bool VisitHero::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
//TODO: VisitObj shoudl not be used for heroes, but...
|
||||
if(goal->goalType == VISIT_TILE)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -30,13 +30,6 @@ namespace Goals
|
||||
objid = hid;
|
||||
priority = 4;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitHero & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -31,65 +29,6 @@ bool VisitObj::operator==(const VisitObj & other) const
|
||||
return other.hero.h == hero.h && other.objid == objid;
|
||||
}
|
||||
|
||||
std::string VisitObj::completeMessage() const
|
||||
{
|
||||
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
|
||||
}
|
||||
|
||||
TGoalVec VisitObj::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec goalList;
|
||||
const CGObjectInstance * obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if(!obj)
|
||||
{
|
||||
throw cannotFulfillGoalException("Object is missing - goal is invalid now!");
|
||||
}
|
||||
|
||||
int3 pos = obj->visitablePos();
|
||||
if(hero)
|
||||
{
|
||||
if(ai->isAccessibleForHero(pos, hero))
|
||||
{
|
||||
if(isSafeToVisit(hero, pos))
|
||||
goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(hero)));
|
||||
else
|
||||
goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
|
||||
|
||||
return goalList;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto potentialVisitor : cb->getHeroesInfo())
|
||||
{
|
||||
if(ai->isAccessibleForHero(pos, potentialVisitor))
|
||||
{
|
||||
if(isSafeToVisit(potentialVisitor, pos))
|
||||
goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(potentialVisitor)));
|
||||
else
|
||||
goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true)));
|
||||
}
|
||||
}
|
||||
if(!goalList.empty())
|
||||
{
|
||||
return goalList;
|
||||
}
|
||||
}
|
||||
|
||||
goalList.push_back(sptr(ClearWayTo(pos)));
|
||||
return goalList;
|
||||
}
|
||||
|
||||
TSubgoal VisitObj::whatToDoToAchieve()
|
||||
{
|
||||
auto bestGoal = fh->chooseSolution(getAllPossibleSubgoals());
|
||||
|
||||
if(bestGoal->goalType == VISIT_OBJ && bestGoal->hero)
|
||||
bestGoal->setisElementar(true);
|
||||
|
||||
return bestGoal;
|
||||
}
|
||||
|
||||
VisitObj::VisitObj(int Objid)
|
||||
: CGoal(VISIT_OBJ)
|
||||
{
|
||||
@ -102,17 +41,3 @@ VisitObj::VisitObj(int Objid)
|
||||
|
||||
priority = 3;
|
||||
}
|
||||
|
||||
bool VisitObj::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if(goal->goalType == VISIT_TILE)
|
||||
{
|
||||
if (!hero || hero == goal->hero)
|
||||
{
|
||||
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if (obj && obj->visitablePos() == goal->tile) //object could be removed
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -23,10 +23,6 @@ namespace Goals
|
||||
VisitObj() = delete; // empty constructor not allowed
|
||||
VisitObj(int Objid);
|
||||
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitObj & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -30,69 +28,3 @@ bool VisitTile::operator==(const VisitTile & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.tile == tile;
|
||||
}
|
||||
|
||||
std::string VisitTile::completeMessage() const
|
||||
{
|
||||
return "Hero " + hero.get()->name + " visited tile " + tile.toString();
|
||||
}
|
||||
|
||||
TSubgoal VisitTile::whatToDoToAchieve()
|
||||
{
|
||||
auto ret = fh->chooseSolution(getAllPossibleSubgoals());
|
||||
|
||||
if(ret->hero)
|
||||
{
|
||||
if(isSafeToVisit(ret->hero, tile) && ai->isAccessibleForHero(tile, ret->hero))
|
||||
{
|
||||
ret->setisElementar(true);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sptr(GatherArmy(fh->evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT)
|
||||
.sethero(ret->hero).setisAbstract(true));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TGoalVec VisitTile::getAllPossibleSubgoals()
|
||||
{
|
||||
assert(cb->isInTheMap(tile));
|
||||
|
||||
TGoalVec ret;
|
||||
if(!cb->isVisible(tile))
|
||||
ret.push_back(sptr(Explore())); //what sense does it make?
|
||||
else
|
||||
{
|
||||
std::vector<const CGHeroInstance *> heroes;
|
||||
if(hero)
|
||||
heroes.push_back(hero.h); //use assigned hero if any
|
||||
else
|
||||
heroes = cb->getHeroesInfo(); //use most convenient hero
|
||||
|
||||
for(auto h : heroes)
|
||||
{
|
||||
if(ai->isAccessibleForHero(tile, h))
|
||||
ret.push_back(sptr(VisitTile(tile).sethero(h)));
|
||||
}
|
||||
if(ai->canRecruitAnyHero())
|
||||
ret.push_back(sptr(RecruitHero()));
|
||||
}
|
||||
if(ret.empty())
|
||||
{
|
||||
auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
|
||||
if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
|
||||
{
|
||||
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
|
||||
ret.push_back(sptr(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
|
||||
}
|
||||
else
|
||||
ret.push_back(sptr(ClearWayTo(tile)));
|
||||
}
|
||||
|
||||
//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
|
||||
return ret;
|
||||
}
|
||||
|
@ -29,9 +29,6 @@ namespace Goals
|
||||
tile = Tile;
|
||||
priority = 5;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitTile & other) const override;
|
||||
};
|
||||
}
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
@ -62,9 +60,9 @@ TSubgoal Win::whatToDoToAchieve()
|
||||
if(goal.object)
|
||||
{
|
||||
auto obj = cb->getObj(goal.object->id);
|
||||
if(obj)
|
||||
/*if(obj)
|
||||
if(obj->getOwner() == ai->playerID) //we can't capture our own object
|
||||
return sptr(Conquer());
|
||||
return sptr(Conquer());*/
|
||||
|
||||
|
||||
return sptr(VisitObj(goal.object->id.getNum()));
|
||||
@ -122,8 +120,8 @@ TSubgoal Win::whatToDoToAchieve()
|
||||
} //TODO: use FIND_OBJ
|
||||
else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
|
||||
return sptr(VisitObj(obj->id.getNum()));
|
||||
else
|
||||
return sptr(Explore());
|
||||
/*else
|
||||
return sptr(Explore());*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -166,7 +164,7 @@ TSubgoal Win::whatToDoToAchieve()
|
||||
break;
|
||||
}
|
||||
case EventCondition::STANDARD_WIN:
|
||||
return sptr(Conquer());
|
||||
return sptr(Invalid());
|
||||
|
||||
// Conditions that likely don't need any implementation
|
||||
case EventCondition::DAYS_PASSED:
|
||||
@ -182,7 +180,7 @@ TSubgoal Win::whatToDoToAchieve()
|
||||
case EventCondition::HAVE_BUILDING_0:
|
||||
case EventCondition::DESTROY_0:
|
||||
//TODO: support new condition format
|
||||
return sptr(Conquer());
|
||||
return sptr(Invalid());
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
@ -64,7 +64,12 @@ boost::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance
|
||||
auto hero = dynamic_cast<const CGHeroInstance*>(obj);
|
||||
return getObjectValue(obj->ID, hero->type->heroClass->id);
|
||||
}
|
||||
if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4)
|
||||
else if(obj->ID == Obj::PRISON)
|
||||
{
|
||||
//special case: in-game prison subID is captured hero ID, but config has one subID with index 0 for normal prison - use that one
|
||||
return getObjectValue(obj->ID, 0);
|
||||
}
|
||||
else if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4)
|
||||
{
|
||||
auto dwelling = dynamic_cast<const CGDwelling *>(obj);
|
||||
int aiValue = 0;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
Goals::TSubgoal BattleAction::whatToDo(const HeroPtr & hero) const
|
||||
Goals::TSubgoal BattleAction::whatToDo(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
|
||||
}
|
||||
|
@ -25,6 +25,6 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
};
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const
|
||||
Goals::TSubgoal BuildBoatAction::whatToDo(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::BuildBoat(shipyard));
|
||||
}
|
||||
@ -27,7 +27,7 @@ namespace AIPathfinding
|
||||
return sourceActor->resourceActor;
|
||||
}
|
||||
|
||||
Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const
|
||||
Goals::TSubgoal SummonBoatAction::whatToDo(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace AIPathfinding
|
||||
class SummonBoatAction : public VirtualBoatAction
|
||||
{
|
||||
public:
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
@ -53,7 +53,7 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const = 0;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
using namespace AIPathfinding;
|
||||
|
||||
Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const
|
||||
Goals::TSubgoal TownPortalAction::whatToDo(const CGHeroInstance * hero) const
|
||||
{
|
||||
const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
|
||||
|
||||
|
@ -28,6 +28,6 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
};
|
||||
}
|
@ -32,76 +32,6 @@ void PathfindingManager::setAI(VCAI * AI)
|
||||
ai = AI;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitTile(const int3 & tile, bool allowGatherArmy) const
|
||||
{
|
||||
Goals::TGoalVec result;
|
||||
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
result.reserve(heroes.size());
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
vstd::concatenate(result, howToVisitTile(hero, tile, allowGatherArmy));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitObj(ObjectIdRef obj, bool allowGatherArmy) const
|
||||
{
|
||||
Goals::TGoalVec result;
|
||||
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
result.reserve(heroes.size());
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
vstd::concatenate(result, howToVisitObj(hero, obj, allowGatherArmy));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
|
||||
{
|
||||
auto result = findPaths(tile, allowGatherArmy, hero, [&](int3 firstTileToGet) -> Goals::TSubgoal
|
||||
{
|
||||
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
|
||||
});
|
||||
|
||||
for(Goals::TSubgoal solution : result)
|
||||
{
|
||||
solution->setparent(sptr(Goals::VisitTile(tile).sethero(hero).setevaluationContext(solution->evaluationContext)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
|
||||
{
|
||||
if(!obj)
|
||||
{
|
||||
return Goals::TGoalVec();
|
||||
}
|
||||
|
||||
int3 dest = obj->visitablePos();
|
||||
|
||||
auto result = findPaths(dest, allowGatherArmy, hero, [&](int3 firstTileToGet) -> Goals::TSubgoal
|
||||
{
|
||||
if(obj->ID.num == Obj::HERO && obj->getOwner() == hero->getOwner())
|
||||
return sptr(Goals::VisitHero(obj->id.getNum()).sethero(hero).setisAbstract(true));
|
||||
else
|
||||
return sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setisAbstract(true));
|
||||
});
|
||||
|
||||
for(Goals::TSubgoal solution : result)
|
||||
{
|
||||
solution->setparent(sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setevaluationContext(solution->evaluationContext)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
auto paths = pathfinder->getPathInfo(tile);
|
||||
@ -118,152 +48,6 @@ std::vector<AIPath> PathfindingManager::getPathsToTile(const int3 & tile) const
|
||||
return pathfinder->getPathInfo(tile);
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::findPaths(
|
||||
crint3 dest,
|
||||
bool allowGatherArmy,
|
||||
HeroPtr hero,
|
||||
const std::function<Goals::TSubgoal(int3)> doVisitTile) const
|
||||
{
|
||||
Goals::TGoalVec result;
|
||||
boost::optional<uint64_t> armyValueRequired;
|
||||
uint64_t danger;
|
||||
|
||||
std::vector<AIPath> chainInfo = pathfinder->getPathInfo(dest);
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Trying to find a way for %s to visit tile %s", hero->name, dest.toString());
|
||||
#endif
|
||||
|
||||
for(auto path : chainInfo)
|
||||
{
|
||||
if((hero && hero.get() != path.targetHero) || path.nodes.empty())
|
||||
continue;
|
||||
|
||||
const AIPathNodeInfo & firstNode = path.firstNode();
|
||||
int3 firstTileToGet = firstNode.coord;
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
std::stringstream str;
|
||||
|
||||
str << "Path found ";
|
||||
|
||||
for(auto node : path.nodes)
|
||||
str << node.targetHero->name << "->" << node.coord.toString() << "; ";
|
||||
|
||||
logAi->trace(str.str());
|
||||
#endif
|
||||
if(ai->isTileNotReserved(hero.get(), firstTileToGet))
|
||||
{
|
||||
danger = path.getTotalDanger();
|
||||
|
||||
if(isSafeToVisit(hero, path.heroArmy, danger))
|
||||
{
|
||||
Goals::TSubgoal solution;
|
||||
|
||||
if(path.specialAction)
|
||||
{
|
||||
solution = path.specialAction->whatToDo(hero);
|
||||
}
|
||||
else
|
||||
{
|
||||
solution = dest == firstTileToGet
|
||||
? doVisitTile(firstTileToGet)
|
||||
: clearWayTo(firstNode.targetHero, firstTileToGet);
|
||||
}
|
||||
|
||||
if(solution->invalid())
|
||||
continue;
|
||||
|
||||
if(solution->evaluationContext.danger < danger)
|
||||
solution->evaluationContext.danger = danger;
|
||||
|
||||
solution->evaluationContext.movementCost += path.movementCost();
|
||||
solution->evaluationContext.armyLoss += path.getTotalArmyLoss();
|
||||
solution->evaluationContext.heroStrength = path.getHeroStrength();
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("It's safe for %s to visit tile %s with danger %s, loss %s, army strength %s, goal %s",
|
||||
hero->name,
|
||||
dest.toString(),
|
||||
std::to_string(danger),
|
||||
std::to_string(path.armyLoss),
|
||||
std::to_string(path.heroArmy->getArmyStrength()),
|
||||
solution->name());
|
||||
#endif
|
||||
result.push_back(solution);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!armyValueRequired || armyValueRequired > danger)
|
||||
{
|
||||
armyValueRequired = boost::make_optional(danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
danger = armyValueRequired.get_value_or(0);
|
||||
|
||||
if(allowGatherArmy && danger > 0)
|
||||
{
|
||||
//we need to get army in order to conquer that place
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Gather army for %s, value=%s", hero->name, std::to_string(danger));
|
||||
#endif
|
||||
result.push_back(sptr(Goals::GatherArmy(danger * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet) const
|
||||
{
|
||||
if(isBlockedBorderGate(firstTileToGet))
|
||||
{
|
||||
//FIXME: this way we'll not visit gate and activate quest :?
|
||||
return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->subID));
|
||||
}
|
||||
|
||||
auto topObj = cb->getTopObj(firstTileToGet);
|
||||
if(topObj)
|
||||
{
|
||||
|
||||
if(vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[hero], topObj))
|
||||
{
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
if(topObj->ID == Obj::HERO && cb->getPlayerRelations(hero->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
|
||||
{
|
||||
if(topObj != hero.get(true)) //the hero we want to free
|
||||
{
|
||||
logAi->warn("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
|
||||
}
|
||||
}
|
||||
|
||||
if(topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
|
||||
{
|
||||
if(shouldVisit(hero, topObj))
|
||||
{
|
||||
//do NOT use VISIT_TILE, as tile with quets guard can't be visited
|
||||
return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero));
|
||||
}
|
||||
|
||||
auto questObj = dynamic_cast<const IQuestObject*>(topObj);
|
||||
|
||||
if(questObj)
|
||||
{
|
||||
auto questInfo = QuestInfo(questObj->quest, topObj, topObj->visitablePos());
|
||||
|
||||
return sptr(Goals::CompleteQuest(questInfo));
|
||||
}
|
||||
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
}
|
||||
|
||||
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
|
||||
}
|
||||
|
||||
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
||||
{
|
||||
logAi->debug("AIPathfinder has been reseted.");
|
||||
|
@ -21,10 +21,6 @@ public:
|
||||
virtual void setAI(VCAI * AI) = 0;
|
||||
|
||||
virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0;
|
||||
virtual Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const = 0;
|
||||
virtual Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
|
||||
virtual Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const = 0;
|
||||
virtual Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
|
||||
virtual std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const = 0;
|
||||
virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0;
|
||||
};
|
||||
@ -42,10 +38,6 @@ public:
|
||||
PathfindingManager() = default;
|
||||
PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
|
||||
|
||||
Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
|
||||
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||
|
@ -26,7 +26,7 @@ namespace AIPathfinding
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override
|
||||
{
|
||||
return Goals::sptr(Goals::Invalid());
|
||||
}
|
||||
|
@ -1,364 +0,0 @@
|
||||
/*
|
||||
* ResourceManager.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 "ResourceManager.h"
|
||||
#include "Goals/Goals.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
#define GOLD_RESERVE (10000); //at least we'll be able to reach capitol
|
||||
|
||||
ResourceObjective::ResourceObjective(const TResources & Res, Goals::TSubgoal Goal)
|
||||
: resources(Res), goal(Goal)
|
||||
{
|
||||
}
|
||||
|
||||
bool ResourceObjective::operator<(const ResourceObjective & ro) const
|
||||
{
|
||||
return goal->priority < ro.goal->priority;
|
||||
}
|
||||
|
||||
ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI)
|
||||
: ai(AI), cb(CB)
|
||||
{
|
||||
}
|
||||
|
||||
void ResourceManager::init(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
cb = CB;
|
||||
}
|
||||
|
||||
void ResourceManager::setAI(VCAI * AI)
|
||||
{
|
||||
ai = AI;
|
||||
}
|
||||
|
||||
bool ResourceManager::canAfford(const TResources & cost) const
|
||||
{
|
||||
return freeResources().canAfford(cost);
|
||||
}
|
||||
|
||||
TResources ResourceManager::estimateIncome() const
|
||||
{
|
||||
TResources ret;
|
||||
for (const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
ret += t->dailyIncome();
|
||||
}
|
||||
|
||||
for (const CGObjectInstance * obj : ai->getFlaggedObjects())
|
||||
{
|
||||
if (obj->ID == Obj::MINE)
|
||||
{
|
||||
switch (obj->subID)
|
||||
{
|
||||
case Res::WOOD:
|
||||
case Res::ORE:
|
||||
ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION;
|
||||
break;
|
||||
case Res::GOLD:
|
||||
case 7: //abandoned mine -> also gold
|
||||
ret[Res::GOLD] += GOLD_MINE_PRODUCTION;
|
||||
break;
|
||||
default:
|
||||
ret[obj->subID] += RESOURCE_MINE_PRODUCTION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ResourceManager::reserveResoures(const TResources & res, Goals::TSubgoal goal)
|
||||
{
|
||||
if (!goal->invalid())
|
||||
tryPush(ResourceObjective(res, goal));
|
||||
else
|
||||
logAi->warn("Attempt to reserve resources for Invalid goal");
|
||||
}
|
||||
|
||||
Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o) const
|
||||
{
|
||||
auto allResources = cb->getResourceAmount();
|
||||
auto income = estimateIncome();
|
||||
Res::ERes resourceType = Res::INVALID;
|
||||
TResource amountToCollect = 0;
|
||||
|
||||
typedef std::pair<Res::ERes, TResource> resPair;
|
||||
std::map<Res::ERes, TResource> missingResources;
|
||||
|
||||
//TODO: unit test for complex resource sets
|
||||
|
||||
//sum missing resources of given type for ALL reserved objectives
|
||||
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
|
||||
{
|
||||
//choose specific resources we need for this goal (not 0)
|
||||
for (auto r = Res::ResourceSet::nziterator(o.resources); r.valid(); r++)
|
||||
missingResources[r->resType] += it->resources[r->resType]; //goal it costs r units of resType
|
||||
}
|
||||
for (auto it = Res::ResourceSet::nziterator(o.resources); it.valid(); it++)
|
||||
{
|
||||
missingResources[it->resType] -= allResources[it->resType]; //missing = (what we need) - (what we have)
|
||||
vstd::amax(missingResources[it->resType], 0); // if we have more resources than reserved, we don't need them
|
||||
}
|
||||
vstd::erase_if(missingResources, [=](const resPair & p) -> bool
|
||||
{
|
||||
return !(p.second); //in case evaluated to 0 or less
|
||||
});
|
||||
if (missingResources.empty()) //FIXME: should be unit-tested out
|
||||
{
|
||||
logAi->error("We don't need to collect resources %s for goal %s", o.resources.toString(), o.goal->name());
|
||||
return o.goal;
|
||||
}
|
||||
|
||||
float goalPriority = 10; //arbitrary, will be divided
|
||||
for (const resPair & p : missingResources)
|
||||
{
|
||||
if (!income[p.first]) //prioritize resources with 0 income
|
||||
{
|
||||
resourceType = p.first;
|
||||
amountToCollect = p.second;
|
||||
goalPriority /= amountToCollect; //need more resources -> lower priority
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (resourceType == Res::INVALID) //no needed resources has 0 income,
|
||||
{
|
||||
//find the one which takes longest to collect
|
||||
typedef std::pair<Res::ERes, float> timePair;
|
||||
std::map<Res::ERes, float> daysToEarn;
|
||||
for (auto it : missingResources)
|
||||
daysToEarn[it.first] = (float)missingResources[it.first] / income[it.first];
|
||||
auto incomeComparer = [&income](const timePair & lhs, const timePair & rhs) -> bool
|
||||
{
|
||||
//theoretically income can be negative, but that falls into this comparison
|
||||
return lhs.second < rhs.second;
|
||||
};
|
||||
|
||||
resourceType = boost::max_element(daysToEarn, incomeComparer)->first;
|
||||
amountToCollect = missingResources[resourceType];
|
||||
goalPriority /= daysToEarn[resourceType]; //more days - lower priority
|
||||
}
|
||||
if (resourceType == Res::GOLD)
|
||||
goalPriority *= 1000;
|
||||
|
||||
//this is abstract goal and might take soem time to complete
|
||||
return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect).setisAbstract(true));
|
||||
}
|
||||
|
||||
Goals::TSubgoal ResourceManager::whatToDo() const //suggest any goal
|
||||
{
|
||||
if (queue.size())
|
||||
{
|
||||
auto o = queue.top();
|
||||
|
||||
auto allResources = cb->getResourceAmount(); //we don't consider savings, it's out top-priority goal
|
||||
if (allResources.canAfford(o.resources))
|
||||
return o.goal;
|
||||
else //we can't afford even top-priority goal, need to collect resources
|
||||
return collectResourcesForOurGoal(o);
|
||||
}
|
||||
else
|
||||
return Goals::sptr(Goals::Invalid()); //nothing else to do
|
||||
}
|
||||
|
||||
Goals::TSubgoal ResourceManager::whatToDo(TResources &res, Goals::TSubgoal goal)
|
||||
{
|
||||
logAi->trace("ResourceManager: checking goal %s which requires resources %s", goal->name(), res.toString());
|
||||
|
||||
TResources accumulatedResources;
|
||||
auto allResources = cb->getResourceAmount();
|
||||
|
||||
ResourceObjective ro(res, goal);
|
||||
tryPush(ro);
|
||||
//check if we can afford all the objectives with higher priority first
|
||||
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
|
||||
{
|
||||
accumulatedResources += it->resources;
|
||||
|
||||
logAi->trace(
|
||||
"ResourceManager: checking goal %s, accumulatedResources=%s, available=%s",
|
||||
it->goal->name(),
|
||||
accumulatedResources.toString(),
|
||||
allResources.toString());
|
||||
|
||||
if(!accumulatedResources.canBeAfforded(allResources))
|
||||
{
|
||||
//can't afford
|
||||
break;
|
||||
}
|
||||
else //can afford all goals up to this point
|
||||
{
|
||||
if(it->goal == goal)
|
||||
{
|
||||
logAi->debug("ResourceManager: can afford goal %s", goal->name());
|
||||
return goal; //can afford immediately
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logAi->debug("ResourceManager: can not afford goal %s", goal->name());
|
||||
|
||||
return collectResourcesForOurGoal(ro);
|
||||
}
|
||||
|
||||
bool ResourceManager::containsObjective(Goals::TSubgoal goal) const
|
||||
{
|
||||
logAi->trace("Entering ResourceManager.containsObjective goal=%s", goal->name());
|
||||
dumpToLog();
|
||||
|
||||
//TODO: unit tests for once
|
||||
for (auto objective : queue)
|
||||
{
|
||||
if (objective.goal == goal)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal)
|
||||
{
|
||||
logAi->trace("Entering ResourceManager.notifyGoalCompleted goal=%s", goal->name());
|
||||
|
||||
if (goal->invalid())
|
||||
logAi->warn("Attempt to complete Invalid goal");
|
||||
|
||||
std::function<bool(const Goals::TSubgoal &)> equivalentGoalsCheck = [goal](const Goals::TSubgoal & x) -> bool
|
||||
{
|
||||
return x == goal || x->fulfillsMe(goal);
|
||||
};
|
||||
|
||||
bool removedGoal = removeOutdatedObjectives(equivalentGoalsCheck);
|
||||
|
||||
dumpToLog();
|
||||
|
||||
return removedGoal;
|
||||
}
|
||||
|
||||
bool ResourceManager::updateGoal(Goals::TSubgoal goal)
|
||||
{
|
||||
//we update priority of goal if it is easier or more difficult to complete
|
||||
if (goal->invalid())
|
||||
logAi->warn("Attempt to update Invalid goal");
|
||||
|
||||
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
|
||||
{
|
||||
return ro.goal == goal;
|
||||
});
|
||||
if (it != queue.end())
|
||||
{
|
||||
it->goal->setpriority(goal->priority);
|
||||
auto handle = queue.s_handle_from_iterator(it);
|
||||
queue.update(handle); //restore order
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResourceManager::dumpToLog() const
|
||||
{
|
||||
for(auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
|
||||
{
|
||||
logAi->trace("ResourceManager contains goal %s which requires resources %s", it->goal->name(), it->resources.toString());
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::tryPush(const ResourceObjective & o)
|
||||
{
|
||||
auto goal = o.goal;
|
||||
|
||||
logAi->trace("ResourceManager: Trying to add goal %s which requires resources %s", goal->name(), o.resources.toString());
|
||||
dumpToLog();
|
||||
|
||||
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
|
||||
{
|
||||
return ro.goal == goal;
|
||||
});
|
||||
if (it != queue.end())
|
||||
{
|
||||
auto handle = queue.s_handle_from_iterator(it);
|
||||
vstd::amax(goal->priority, it->goal->priority); //increase priority if case
|
||||
//update resources with new value
|
||||
queue.update(handle, ResourceObjective(o.resources, goal)); //restore order
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue.push(o); //add new objective
|
||||
logAi->debug("Reserved resources (%s) for %s", o.resources.toString(), goal->name());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::hasTasksLeft() const
|
||||
{
|
||||
return !queue.empty();
|
||||
}
|
||||
|
||||
bool ResourceManager::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate)
|
||||
{
|
||||
bool removedAnything = false;
|
||||
while(true)
|
||||
{ //unfortunately we can't use remove_if on heap
|
||||
auto it = boost::find_if(queue, [&](const ResourceObjective & ro) -> bool
|
||||
{
|
||||
return predicate(ro.goal);
|
||||
});
|
||||
|
||||
if(it != queue.end()) //removed at least one
|
||||
{
|
||||
logAi->debug("Removing goal %s from ResourceManager.", it->goal->name());
|
||||
queue.erase(queue.s_handle_from_iterator(it));
|
||||
removedAnything = true;
|
||||
}
|
||||
else
|
||||
{ //found nothing more to remove
|
||||
break;
|
||||
}
|
||||
}
|
||||
return removedAnything;
|
||||
}
|
||||
|
||||
TResources ResourceManager::reservedResources() const
|
||||
{
|
||||
TResources res;
|
||||
for (auto it : queue) //substract the value of reserved goals
|
||||
res += it.resources;
|
||||
return res;
|
||||
}
|
||||
|
||||
TResources ResourceManager::freeResources() const
|
||||
{
|
||||
TResources myRes = cb->getResourceAmount();
|
||||
myRes -= reservedResources(); //substract the value of reserved goals
|
||||
|
||||
for (auto & val : myRes)
|
||||
vstd::amax(val, 0); //never negative
|
||||
|
||||
return myRes;
|
||||
}
|
||||
|
||||
TResource ResourceManager::freeGold() const
|
||||
{
|
||||
return freeResources()[Res::GOLD];
|
||||
}
|
||||
|
||||
TResources ResourceManager::allResources() const
|
||||
{
|
||||
return cb->getResourceAmount();
|
||||
}
|
||||
|
||||
TResource ResourceManager::allGold() const
|
||||
{
|
||||
return cb->getResourceAmount()[Res::GOLD];
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* ResourceManager.h, 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "AIUtility.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "VCAI.h"
|
||||
#include <boost/heap/binomial_heap.hpp>
|
||||
|
||||
class AIhelper;
|
||||
class IResourceManager;
|
||||
|
||||
struct DLL_EXPORT ResourceObjective
|
||||
{
|
||||
ResourceObjective() = default;
|
||||
ResourceObjective(const TResources &res, Goals::TSubgoal goal);
|
||||
bool operator < (const ResourceObjective &ro) const;
|
||||
|
||||
TResources resources; //how many resoures do we need
|
||||
Goals::TSubgoal goal; //what for (build, gather army etc...)
|
||||
|
||||
//TODO: register?
|
||||
template<typename Handler> void serializeInternal(Handler & h, const int version)
|
||||
{
|
||||
h & resources;
|
||||
//h & goal; //FIXME: goal serialization is broken
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_EXPORT IResourceManager //: public: IAbstractManager
|
||||
{
|
||||
public:
|
||||
virtual ~IResourceManager() = default;
|
||||
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
||||
virtual void setAI(VCAI * AI) = 0;
|
||||
|
||||
virtual TResources reservedResources() const = 0;
|
||||
virtual TResources freeResources() const = 0;
|
||||
virtual TResource freeGold() const = 0;
|
||||
virtual TResources allResources() const = 0;
|
||||
virtual TResource allGold() const = 0;
|
||||
|
||||
virtual Goals::TSubgoal whatToDo() const = 0;//get highest-priority goal
|
||||
virtual Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) = 0;
|
||||
virtual bool containsObjective(Goals::TSubgoal goal) const = 0;
|
||||
virtual bool hasTasksLeft() const = 0;
|
||||
virtual bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) = 0; //remove ResourceObjectives from queue if ResourceObjective->goal meets specific criteria
|
||||
virtual bool notifyGoalCompleted(Goals::TSubgoal goal) = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT ResourceManager : public IResourceManager
|
||||
{
|
||||
/*Resource Manager is a smart shopping list for AI to help
|
||||
Uses priority queue based on CGoal.priority */
|
||||
friend class VCAI;
|
||||
friend class AIhelper;
|
||||
friend struct SetGlobalState;
|
||||
|
||||
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
|
||||
VCAI * ai;
|
||||
|
||||
public:
|
||||
ResourceManager() = default;
|
||||
ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
|
||||
|
||||
bool canAfford(const TResources & cost) const;
|
||||
TResources reservedResources() const override;
|
||||
TResources freeResources() const override;
|
||||
TResource freeGold() const override;
|
||||
TResources allResources() const override;
|
||||
TResource allGold() const override;
|
||||
|
||||
Goals::TSubgoal whatToDo() const override; //peek highest-priority goal
|
||||
Goals::TSubgoal whatToDo(TResources & res, Goals::TSubgoal goal) override; //can we afford this goal or need to CollectRes?
|
||||
bool containsObjective(Goals::TSubgoal goal) const override;
|
||||
bool hasTasksLeft() const override;
|
||||
bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
|
||||
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
|
||||
|
||||
protected: //not-const actions only for AI
|
||||
virtual void reserveResoures(const TResources & res, Goals::TSubgoal goal = Goals::TSubgoal());
|
||||
virtual bool updateGoal(Goals::TSubgoal goal); //new goal must have same properties but different priority
|
||||
virtual bool tryPush(const ResourceObjective &o);
|
||||
|
||||
//inner processing
|
||||
virtual TResources estimateIncome() const;
|
||||
virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const;
|
||||
|
||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
|
||||
private:
|
||||
TResources saving;
|
||||
|
||||
boost::heap::binomial_heap<ResourceObjective> queue;
|
||||
|
||||
void dumpToLog() const;
|
||||
|
||||
//TODO: register?
|
||||
template<typename Handler> void serializeInternal(Handler & h, const int version)
|
||||
{
|
||||
h & saving;
|
||||
h & queue;
|
||||
}
|
||||
};
|
@ -76,7 +76,7 @@
|
||||
<Add option="-Wno-sign-compare" />
|
||||
<Add option="-Wno-unused-parameter" />
|
||||
<Add option="-Wno-overloaded-virtual" />
|
||||
<Add option="-DBOOST_THREAD_USE_LIB" />
|
||||
<Add option="-DBOOST_ALL_DYN_LINK" />
|
||||
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
|
||||
<Add option="-D_WIN32_WINNT=0x0501" />
|
||||
<Add option="-D_WIN32" />
|
||||
@ -93,6 +93,8 @@
|
||||
<Unit filename="AIUtility.h" />
|
||||
<Unit filename="AIhelper.cpp" />
|
||||
<Unit filename="AIhelper.h" />
|
||||
<Unit filename="ArmyManager.cpp" />
|
||||
<Unit filename="ArmyManager.h" />
|
||||
<Unit filename="BuildingManager.cpp" />
|
||||
<Unit filename="BuildingManager.h" />
|
||||
<Unit filename="FuzzyEngines.cpp" />
|
||||
|
@ -10,8 +10,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "VCAI.h"
|
||||
#include "FuzzyHelper.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "BuildingManager.h"
|
||||
#include "Goals/Goals.h"
|
||||
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
@ -119,9 +117,6 @@ void VCAI::heroMoved(const TryMoveHero & details)
|
||||
}
|
||||
}
|
||||
}
|
||||
//FIXME: teleports are not correctly visited
|
||||
unreserveObject(hero, t1);
|
||||
unreserveObject(hero, t2);
|
||||
}
|
||||
else if(details.result == TryMoveHero::EMBARK && hero)
|
||||
{
|
||||
@ -246,8 +241,6 @@ void VCAI::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * vi
|
||||
if(start && visitedObj) //we can end visit with null object, anyway
|
||||
{
|
||||
markObjectVisited(visitedObj);
|
||||
unreserveObject(visitor, visitedObj);
|
||||
completeGoal(sptr(Goals::VisitObj(visitedObj->id.getNum()).sethero(visitor))); //we don't need to visit it anymore
|
||||
//TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..)
|
||||
if (visitedObj->ID == Obj::HERO)
|
||||
{
|
||||
@ -350,9 +343,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
|
||||
transferFrom2to1(secondHero, firstHero);
|
||||
}
|
||||
|
||||
completeGoal(sptr(Goals::VisitHero(firstHero->id.getNum()))); //TODO: what if we were visited by other hero in the meantime?
|
||||
completeGoal(sptr(Goals::VisitHero(secondHero->id.getNum())));
|
||||
|
||||
answerQuery(query, 0);
|
||||
});
|
||||
}
|
||||
@ -399,9 +389,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
|
||||
vstd::erase_if_present(visitableObjs, obj);
|
||||
vstd::erase_if_present(alreadyVisited, obj);
|
||||
|
||||
for(auto h : cb->getHeroesInfo())
|
||||
unreserveObject(h, obj);
|
||||
|
||||
std::function<bool(const Goals::TSubgoal &)> checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool
|
||||
{
|
||||
if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum()))
|
||||
@ -412,27 +399,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
|
||||
return false;
|
||||
};
|
||||
|
||||
//clear VCAI / main loop caches
|
||||
vstd::erase_if(lockedHeroes, [&](const std::pair<HeroPtr, Goals::TSubgoal> & x) -> bool
|
||||
{
|
||||
return checkRemovalValidity(x.second);
|
||||
});
|
||||
|
||||
vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool
|
||||
{
|
||||
return checkRemovalValidity(x.first);
|
||||
});
|
||||
|
||||
vstd::erase_if(basicGoals, checkRemovalValidity);
|
||||
vstd::erase_if(goalsToAdd, checkRemovalValidity);
|
||||
vstd::erase_if(goalsToRemove, checkRemovalValidity);
|
||||
|
||||
for(auto goal : ultimateGoalsFromBasic)
|
||||
vstd::erase_if(goal.second, checkRemovalValidity);
|
||||
|
||||
//clear resource manager goal cache
|
||||
ah->removeOutdatedObjectives(checkRemovalValidity);
|
||||
|
||||
//TODO: Find better way to handle hero boat removal
|
||||
if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
|
||||
{
|
||||
@ -440,9 +406,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
|
||||
{
|
||||
vstd::erase_if_present(visitableObjs, hero->boat);
|
||||
vstd::erase_if_present(alreadyVisited, hero->boat);
|
||||
|
||||
for(auto h : cb->getHeroesInfo())
|
||||
unreserveObject(h, hero->boat);
|
||||
}
|
||||
}
|
||||
|
||||
@ -569,9 +532,6 @@ void VCAI::buildChanged(const CGTownInstance * town, BuildingID buildingID, int
|
||||
{
|
||||
LOG_TRACE_PARAMS(logAi, "what '%i'", what);
|
||||
NET_EVENT_HANDLER;
|
||||
|
||||
if(town->getOwner() == playerID && what == 1) //built
|
||||
completeGoal(sptr(Goals::BuildThis(buildingID, town)));
|
||||
}
|
||||
|
||||
void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain)
|
||||
@ -854,23 +814,8 @@ void VCAI::makeTurn()
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(nullkiller)
|
||||
{
|
||||
nullkiller->makeTurn();
|
||||
}
|
||||
else
|
||||
{
|
||||
//it looks messy here, but it's better to have armed heroes before attempting realizing goals
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
moveCreaturesToHero(t);
|
||||
|
||||
mainLoop();
|
||||
|
||||
/*Below function is also responsible for hero movement via internal wander function. By design it is separate logic for heroes that have nothing to do.
|
||||
Heroes that were not picked by striveToGoal(sptr(Goals::Win())); recently (so they do not have new goals and cannot continue/reevaluate previously locked goals) will do logic in wander().*/
|
||||
performTypicalActions();
|
||||
}
|
||||
|
||||
//for debug purpose
|
||||
for (auto h : cb->getHeroesInfo())
|
||||
@ -904,194 +849,6 @@ std::vector<HeroPtr> VCAI::getMyHeroes() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VCAI::mainLoop()
|
||||
{
|
||||
std::vector<Goals::TSubgoal> elementarGoals; //no duplicates allowed (operator ==)
|
||||
basicGoals.clear();
|
||||
|
||||
validateVisitableObjs();
|
||||
|
||||
//get all potential and saved goals
|
||||
//TODO: not lose
|
||||
basicGoals.push_back(sptr(Goals::Win()));
|
||||
for (auto goalPair : lockedHeroes)
|
||||
{
|
||||
fh->setPriority(goalPair.second); //re-evaluate, as heroes moved in the meantime
|
||||
basicGoals.push_back(goalPair.second);
|
||||
}
|
||||
if (ah->hasTasksLeft())
|
||||
basicGoals.push_back(ah->whatToDo());
|
||||
for (auto quest : myCb->getMyQuests())
|
||||
{
|
||||
basicGoals.push_back(sptr(Goals::CompleteQuest(quest)));
|
||||
}
|
||||
basicGoals.push_back(sptr(Goals::Build()));
|
||||
|
||||
invalidPathHeroes.clear();
|
||||
|
||||
while (basicGoals.size())
|
||||
{
|
||||
vstd::removeDuplicates(basicGoals); //TODO: container which does this automagically without has would be nice
|
||||
goalsToAdd.clear();
|
||||
goalsToRemove.clear();
|
||||
elementarGoals.clear();
|
||||
ultimateGoalsFromBasic.clear();
|
||||
|
||||
ah->updatePaths(getMyHeroes());
|
||||
|
||||
logAi->debug("Main loop: decomposing %i basic goals", basicGoals.size());
|
||||
|
||||
for (auto basicGoal : basicGoals)
|
||||
{
|
||||
logAi->debug("Main loop: decomposing basic goal %s", basicGoal->name());
|
||||
|
||||
auto goalToDecompose = basicGoal;
|
||||
Goals::TSubgoal elementarGoal = sptr(Goals::Invalid());
|
||||
int maxAbstractGoals = 10;
|
||||
while (!elementarGoal->isElementar && maxAbstractGoals)
|
||||
{
|
||||
try
|
||||
{
|
||||
elementarGoal = decomposeGoal(goalToDecompose);
|
||||
}
|
||||
catch (goalFulfilledException & e)
|
||||
{
|
||||
//it is impossible to continue some goals (like exploration, for example)
|
||||
//complete abstract goal for now, but maybe main goal finds another path
|
||||
logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name());
|
||||
completeGoal(e.goal); //put in goalsToRemove
|
||||
break;
|
||||
}
|
||||
catch(cannotFulfillGoalException & e)
|
||||
{
|
||||
//it is impossible to continue some goals (like exploration, for example)
|
||||
//complete abstract goal for now, but maybe main goal finds another path
|
||||
goalsToRemove.push_back(basicGoal);
|
||||
logAi->debug("Goal %s decomposition failed: %s", goalToDecompose->name(), e.what());
|
||||
break;
|
||||
}
|
||||
catch (std::exception & e) //decomposition failed, which means we can't decompose entire tree
|
||||
{
|
||||
goalsToRemove.push_back(basicGoal);
|
||||
logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what());
|
||||
break;
|
||||
}
|
||||
if (elementarGoal->isAbstract) //we can decompose it further
|
||||
{
|
||||
goalsToAdd.push_back(elementarGoal);
|
||||
//decompose further now - this is necesssary if we can't add over 10 goals in the pool
|
||||
goalToDecompose = elementarGoal;
|
||||
//there is a risk of infinite abstract goal loop, though it indicates failed logic
|
||||
maxAbstractGoals--;
|
||||
}
|
||||
else if (elementarGoal->isElementar) //should be
|
||||
{
|
||||
logAi->debug("Found elementar goal %s", elementarGoal->name());
|
||||
elementarGoals.push_back(elementarGoal);
|
||||
ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal?
|
||||
break;
|
||||
}
|
||||
else //should never be here
|
||||
throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name());
|
||||
}
|
||||
}
|
||||
|
||||
logAi->trace("Main loop: selecting best elementar goal");
|
||||
|
||||
//now choose one elementar goal to realize
|
||||
Goals::TGoalVec possibleGoals(elementarGoals.begin(), elementarGoals.end()); //copy to vector
|
||||
Goals::TSubgoal goalToRealize = sptr(Goals::Invalid());
|
||||
while (possibleGoals.size())
|
||||
{
|
||||
//allow assign goals to heroes with 0 movement, but don't realize them
|
||||
//maybe there are beter ones left
|
||||
|
||||
auto bestGoal = fh->chooseSolution(possibleGoals);
|
||||
if (bestGoal->hero) //lock this hero to fulfill goal
|
||||
{
|
||||
setGoal(bestGoal->hero, bestGoal);
|
||||
if (!bestGoal->hero->movement || vstd::contains(invalidPathHeroes, bestGoal->hero))
|
||||
{
|
||||
if (!vstd::erase_if_present(possibleGoals, bestGoal))
|
||||
{
|
||||
logAi->error("erase_if_preset failed? Something very wrong!");
|
||||
break;
|
||||
}
|
||||
continue; //chose next from the list
|
||||
}
|
||||
}
|
||||
goalToRealize = bestGoal; //we found our goal to execute
|
||||
break;
|
||||
}
|
||||
|
||||
//realize best goal
|
||||
if (!goalToRealize->invalid())
|
||||
{
|
||||
logAi->debug("Trying to realize %s (value %2.3f)", goalToRealize->name(), goalToRealize->priority);
|
||||
|
||||
try
|
||||
{
|
||||
boost::this_thread::interruption_point();
|
||||
goalToRealize->accept(this); //visitor pattern
|
||||
boost::this_thread::interruption_point();
|
||||
}
|
||||
catch (boost::thread_interrupted & e)
|
||||
{
|
||||
logAi->debug("Player %d: Making turn thread received an interruption!", playerID);
|
||||
throw; //rethrow, we want to truly end this thread
|
||||
}
|
||||
catch (goalFulfilledException & e)
|
||||
{
|
||||
//the sub-goal was completed successfully
|
||||
completeGoal(e.goal);
|
||||
//local goal was also completed?
|
||||
completeGoal(goalToRealize);
|
||||
|
||||
// remove abstract visit tile if we completed the elementar one
|
||||
vstd::erase_if_present(goalsToAdd, goalToRealize);
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
logAi->debug("Failed to realize subgoal of type %s, I will stop.", goalToRealize->name());
|
||||
logAi->debug("The error message was: %s", e.what());
|
||||
|
||||
//erase base goal if we failed to execute decomposed goal
|
||||
for (auto basicGoal : ultimateGoalsFromBasic[goalToRealize])
|
||||
goalsToRemove.push_back(basicGoal);
|
||||
|
||||
// sometimes resource manager contains an elementar goal which is not able to execute anymore and just fails each turn.
|
||||
ai->ah->notifyGoalCompleted(goalToRealize);
|
||||
|
||||
//we failed to realize best goal, but maybe others are still possible?
|
||||
}
|
||||
|
||||
//remove goals we couldn't decompose
|
||||
for (auto goal : goalsToRemove)
|
||||
vstd::erase_if_present(basicGoals, goal);
|
||||
|
||||
//add abstract goals
|
||||
boost::sort(goalsToAdd, [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
|
||||
{
|
||||
return lhs->priority > rhs->priority; //highest priority at the beginning
|
||||
});
|
||||
|
||||
//max number of goals = 10
|
||||
int i = 0;
|
||||
while (basicGoals.size() < 10 && goalsToAdd.size() > i)
|
||||
{
|
||||
if (!vstd::contains(basicGoals, goalsToAdd[i])) //don't add duplicates
|
||||
basicGoals.push_back(goalsToAdd[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else //no elementar goals possible
|
||||
{
|
||||
logAi->debug("Goal decomposition exhausted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
||||
{
|
||||
LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString());
|
||||
@ -1099,7 +856,6 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
||||
{
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
recruitCreatures(dynamic_cast<const CGDwelling *>(obj), h.get());
|
||||
checkHeroArmy(h);
|
||||
break;
|
||||
case Obj::TOWN:
|
||||
if(h->visitedTown) //we are inside, not just attacking
|
||||
@ -1116,7 +872,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
||||
ah->update();
|
||||
}
|
||||
|
||||
if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
|
||||
if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST)
|
||||
{
|
||||
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
|
||||
@ -1127,7 +883,6 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
||||
makePossibleUpgrades(h.get());
|
||||
break;
|
||||
}
|
||||
completeGoal(sptr(Goals::VisitObj(obj->id.getNum()).sethero(h)));
|
||||
}
|
||||
|
||||
void VCAI::moveCreaturesToHero(const CGTownInstance * t)
|
||||
@ -1197,10 +952,6 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme
|
||||
}
|
||||
|
||||
//TODO - having now strongest possible army, we may want to think about arranging stacks
|
||||
|
||||
auto hero = dynamic_cast<const CGHeroInstance *>(destinationArmy);
|
||||
if(hero)
|
||||
checkHeroArmy(hero);
|
||||
}
|
||||
|
||||
void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
|
||||
@ -1313,7 +1064,7 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
|
||||
int count = d->creatures[i].first;
|
||||
CreatureID creID = d->creatures[i].second.back();
|
||||
|
||||
vstd::amin(count, ah->freeResources() / VLC->creh->creatures[creID]->cost);
|
||||
vstd::amin(count, cb->getResourceAmount() / VLC->creh->creatures[creID]->cost);
|
||||
if(count > 0)
|
||||
cb->recruitCreatures(d, recruiter, creID, count, i);
|
||||
}
|
||||
@ -1402,250 +1153,6 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void VCAI::wander(HeroPtr h)
|
||||
{
|
||||
auto visitTownIfAny = [this](HeroPtr h) -> bool
|
||||
{
|
||||
if (h->visitedTown)
|
||||
{
|
||||
townVisitsThisWeek[h].insert(h->visitedTown);
|
||||
buildArmyIn(h->visitedTown);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
//unclaim objects that are now dangerous for us
|
||||
auto reservedObjsSetCopy = reservedHeroesMap[h];
|
||||
for(auto obj : reservedObjsSetCopy)
|
||||
{
|
||||
if(!isSafeToVisit(h, obj->visitablePos()))
|
||||
unreserveObject(h, obj);
|
||||
}
|
||||
|
||||
TimeCheck tc("looking for wander destination");
|
||||
|
||||
while(h->movement)
|
||||
{
|
||||
validateVisitableObjs();
|
||||
ah->updatePaths(getMyHeroes());
|
||||
|
||||
std::vector<ObjectIdRef> dests;
|
||||
|
||||
//also visit our reserved objects - but they are not prioritized to avoid running back and forth
|
||||
vstd::copy_if(reservedHeroesMap[h], std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
|
||||
{
|
||||
return ah->isTileAccessible(h, obj->visitablePos());
|
||||
});
|
||||
|
||||
int pass = 0;
|
||||
std::vector<boost::optional<float>> distanceLimits =
|
||||
{
|
||||
1.0,
|
||||
2.0,
|
||||
boost::none
|
||||
};
|
||||
|
||||
while(!dests.size() && pass < distanceLimits.size())
|
||||
{
|
||||
auto & distanceLimit = distanceLimits[pass];
|
||||
|
||||
logAi->debug("Looking for wander destination pass=%i, cost limit=%f", pass, distanceLimit.get_value_or(-1.0));
|
||||
|
||||
vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
|
||||
{
|
||||
return isGoodForVisit(obj, h, distanceLimit);
|
||||
});
|
||||
|
||||
pass++;
|
||||
}
|
||||
|
||||
if(!dests.size())
|
||||
{
|
||||
logAi->debug("Looking for town destination");
|
||||
|
||||
if(cb->getVisitableObjs(h->visitablePos()).size() > 1)
|
||||
moveHeroToTile(h->visitablePos(), h); //just in case we're standing on blocked subterranean gate
|
||||
|
||||
auto compareReinforcements = [&](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool
|
||||
{
|
||||
const CGHeroInstance * hptr = h.get();
|
||||
auto r1 = ah->howManyReinforcementsCanGet(hptr, lhs),
|
||||
r2 = ah->howManyReinforcementsCanGet(hptr, rhs);
|
||||
if (r1 != r2)
|
||||
return r1 < r2;
|
||||
else
|
||||
return ah->howManyReinforcementsCanBuy(hptr, lhs) < ah->howManyReinforcementsCanBuy(hptr, rhs);
|
||||
};
|
||||
|
||||
std::vector<const CGTownInstance *> townsReachable;
|
||||
std::vector<const CGTownInstance *> townsNotReachable;
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
if(!t->visitingHero && !vstd::contains(townVisitsThisWeek[h], t))
|
||||
{
|
||||
if(isAccessibleForHero(t->visitablePos(), h))
|
||||
townsReachable.push_back(t);
|
||||
else
|
||||
townsNotReachable.push_back(t);
|
||||
}
|
||||
}
|
||||
if(townsReachable.size()) //travel to town with largest garrison, or empty - better than nothing
|
||||
{
|
||||
dests.push_back(*boost::max_element(townsReachable, compareReinforcements));
|
||||
}
|
||||
else if(townsNotReachable.size())
|
||||
{
|
||||
//TODO pick the truly best
|
||||
const CGTownInstance * t = *boost::max_element(townsNotReachable, compareReinforcements);
|
||||
logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->name, t->name, t->visitablePos().toString());
|
||||
int3 pos1 = h->pos;
|
||||
striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h))); //TODO: drop "strive", add to mainLoop
|
||||
//if out hero is stuck, we may need to request another hero to clear the way we see
|
||||
|
||||
if(pos1 == h->pos && h == primaryHero()) //hero can't move
|
||||
{
|
||||
if(canRecruitAnyHero(t))
|
||||
recruitHero(t);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if(cb->getResourceAmount(Res::GOLD) >= GameConstants::HERO_GOLD_COST)
|
||||
{
|
||||
std::vector<const CGTownInstance *> towns = cb->getTownsInfo();
|
||||
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
||||
{
|
||||
for(const CGHeroInstance * h : cb->getHeroesInfo())
|
||||
{
|
||||
if(!t->getArmyStrength() || ah->howManyReinforcementsCanGet(h, t))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (towns.size())
|
||||
{
|
||||
recruitHero(*boost::max_element(towns, compareArmyStrength));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
logAi->debug("Nowhere more to go...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
//end of objs empty
|
||||
|
||||
if(dests.size()) //performance improvement
|
||||
{
|
||||
Goals::TGoalVec targetObjectGoals;
|
||||
for(auto destination : dests)
|
||||
{
|
||||
vstd::concatenate(targetObjectGoals, ah->howToVisitObj(h, destination, false));
|
||||
}
|
||||
|
||||
if(targetObjectGoals.size())
|
||||
{
|
||||
auto bestObjectGoal = fh->chooseSolution(targetObjectGoals);
|
||||
|
||||
//wander should not cause heroes to be reserved - they are always considered free
|
||||
if(bestObjectGoal->goalType == Goals::VISIT_OBJ)
|
||||
{
|
||||
auto chosenObject = cb->getObjInstance(ObjectInstanceID(bestObjectGoal->objid));
|
||||
if(chosenObject != nullptr)
|
||||
logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->pos.toString());
|
||||
}
|
||||
else
|
||||
logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name());
|
||||
|
||||
try
|
||||
{
|
||||
decomposeGoal(bestObjectGoal)->accept(this);
|
||||
}
|
||||
catch(const goalFulfilledException & e)
|
||||
{
|
||||
if(e.goal->goalType == Goals::EGoals::VISIT_TILE || e.goal->goalType == Goals::EGoals::VISIT_OBJ)
|
||||
continue;
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logAi->debug("Nowhere more to go...");
|
||||
break;
|
||||
}
|
||||
|
||||
visitTownIfAny(h);
|
||||
}
|
||||
}
|
||||
|
||||
visitTownIfAny(h); //in case hero is just sitting in town
|
||||
}
|
||||
|
||||
void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
|
||||
{
|
||||
if(goal->invalid())
|
||||
{
|
||||
vstd::erase_if_present(lockedHeroes, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
lockedHeroes[h] = goal;
|
||||
goal->setisElementar(false); //Force always evaluate goals before realizing
|
||||
}
|
||||
}
|
||||
void VCAI::evaluateGoal(HeroPtr h)
|
||||
{
|
||||
if(vstd::contains(lockedHeroes, h))
|
||||
fh->setPriority(lockedHeroes[h]);
|
||||
}
|
||||
|
||||
void VCAI::completeGoal(Goals::TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == Goals::WIN) //we can never complete this goal - unless we already won
|
||||
return;
|
||||
|
||||
logAi->debug("Completing goal: %s", goal->name());
|
||||
|
||||
//notify Managers
|
||||
ah->notifyGoalCompleted(goal);
|
||||
//notify mainLoop()
|
||||
goalsToRemove.push_back(goal); //will be removed from mainLoop() goals
|
||||
for (auto basicGoal : basicGoals) //we could luckily fulfill any of our goals
|
||||
{
|
||||
if (basicGoal->fulfillsMe(goal))
|
||||
goalsToRemove.push_back(basicGoal);
|
||||
}
|
||||
|
||||
//unreserve heroes
|
||||
if(const CGHeroInstance * h = goal->hero.get(true))
|
||||
{
|
||||
auto it = lockedHeroes.find(h);
|
||||
if(it != lockedHeroes.end())
|
||||
{
|
||||
if(it->second == goal || it->second->fulfillsMe(goal)) //FIXME this is overspecified, fulfillsMe shoudl be complete
|
||||
{
|
||||
logAi->debug(goal->completeMessage());
|
||||
lockedHeroes.erase(it); //goal fulfilled, free hero
|
||||
}
|
||||
}
|
||||
}
|
||||
else //complete goal for all heroes maybe?
|
||||
{
|
||||
vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p)
|
||||
{
|
||||
if(p.second == goal || p.second->fulfillsMe(goal)) //we could have fulfilled goals of other heroes by chance
|
||||
{
|
||||
logAi->debug(p.second->completeMessage());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
|
||||
{
|
||||
NET_EVENT_HANDLER;
|
||||
@ -1686,19 +1193,6 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
|
||||
alreadyVisited.insert(obj);
|
||||
}
|
||||
|
||||
void VCAI::reserveObject(HeroPtr h, const CGObjectInstance * obj)
|
||||
{
|
||||
reservedObjs.insert(obj);
|
||||
reservedHeroesMap[h].insert(obj);
|
||||
logAi->debug("reserved object id=%d; address=%p; name=%s", obj->id, obj, obj->getObjectName());
|
||||
}
|
||||
|
||||
void VCAI::unreserveObject(HeroPtr h, const CGObjectInstance * obj)
|
||||
{
|
||||
vstd::erase_if_present(reservedObjs, obj); //unreserve objects
|
||||
vstd::erase_if_present(reservedHeroesMap[h], obj);
|
||||
}
|
||||
|
||||
void VCAI::markHeroUnableToExplore(HeroPtr h)
|
||||
{
|
||||
heroesUnableToExplore.insert(h);
|
||||
@ -2053,25 +1547,15 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
||||
}
|
||||
if(h) //we could have lost hero after last move
|
||||
{
|
||||
completeGoal(sptr(Goals::VisitTile(dst).sethero(h))); //we stepped on some tile, anyway
|
||||
completeGoal(sptr(Goals::ClearWayTo(dst).sethero(h)));
|
||||
|
||||
ret = ret || (dst == h->visitablePos());
|
||||
|
||||
if(!ret) //reserve object we are heading towards
|
||||
{
|
||||
auto obj = vstd::frontOrNull(cb->getVisitableObjs(dst));
|
||||
if(obj && obj != *h)
|
||||
reserveObject(h, obj);
|
||||
}
|
||||
|
||||
if(startHpos == h->visitablePos() && !ret) //we didn't move and didn't reach the target
|
||||
{
|
||||
vstd::erase_if_present(lockedHeroes, h); //hero seemingly is confused or has only 95mp which is not enough to move
|
||||
invalidPathHeroes.insert(h);
|
||||
throw cannotFulfillGoalException("Invalid path found!");
|
||||
}
|
||||
evaluateGoal(h); //new hero position means new game situation
|
||||
|
||||
logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos.toString(), h->visitablePos().toString(), ret);
|
||||
}
|
||||
return ret;
|
||||
@ -2181,7 +1665,6 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
|
||||
if(g.hero->diggingStatus() == EDiggingStatus::CAN_DIG)
|
||||
{
|
||||
cb->dig(g.hero.get());
|
||||
completeGoal(sptr(g)); // finished digging
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2192,7 +1675,7 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
|
||||
|
||||
void VCAI::tryRealize(Goals::Trade & g) //trade
|
||||
{
|
||||
if(ah->freeResources()[g.resID] >= g.value) //goal is already fulfilled. Why we need this check, anyway?
|
||||
if(cb->getResourceAmount((Res::ERes)g.resID) >= g.value) //goal is already fulfilled. Why we need this check, anyway?
|
||||
throw goalFulfilledException(sptr(g));
|
||||
|
||||
int accquiredResources = 0;
|
||||
@ -2200,7 +1683,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
|
||||
{
|
||||
if(const IMarket * m = IMarket::castFrom(obj, false))
|
||||
{
|
||||
auto freeRes = ah->freeResources(); //trade only resources which are not reserved
|
||||
auto freeRes = cb->getResourceAmount(); //trade only resources which are not reserved
|
||||
for(auto it = Res::ResourceSet::nziterator(freeRes); it.valid(); it++)
|
||||
{
|
||||
auto res = it->resType;
|
||||
@ -2217,7 +1700,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
|
||||
accquiredResources = toGet * (it->resVal / toGive);
|
||||
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
|
||||
}
|
||||
if (ah->freeResources()[g.resID] >= g.value)
|
||||
if (cb->getResourceAmount((Res::ERes)g.resID) >= g.value)
|
||||
throw goalFulfilledException(sptr(g)); //we traded all we needed
|
||||
}
|
||||
|
||||
@ -2255,7 +1738,7 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
|
||||
|
||||
for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
|
||||
{
|
||||
auto res = ah->allResources();
|
||||
auto res = cb->getResourceAmount();
|
||||
auto & ci = armyToBuy[i];
|
||||
|
||||
if(g.objid != -1 && ci.creID != g.objid)
|
||||
@ -2365,140 +1848,6 @@ void VCAI::endTurn()
|
||||
logGlobal->info("Player %d (%s) ended turn", playerID, playerID.getStr());
|
||||
}
|
||||
|
||||
void VCAI::striveToGoal(Goals::TSubgoal basicGoal)
|
||||
{
|
||||
//TODO: this function is deprecated and should be dropped altogether
|
||||
|
||||
auto goalToDecompose = basicGoal;
|
||||
Goals::TSubgoal elementarGoal = sptr(Goals::Invalid());
|
||||
int maxAbstractGoals = 10;
|
||||
while (!elementarGoal->isElementar && maxAbstractGoals)
|
||||
{
|
||||
try
|
||||
{
|
||||
elementarGoal = decomposeGoal(goalToDecompose);
|
||||
}
|
||||
catch (goalFulfilledException & e)
|
||||
{
|
||||
//it is impossible to continue some goals (like exploration, for example)
|
||||
completeGoal(e.goal); //put in goalsToRemove
|
||||
logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name());
|
||||
return;
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
goalsToRemove.push_back(basicGoal);
|
||||
logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what());
|
||||
return;
|
||||
}
|
||||
if (elementarGoal->isAbstract) //we can decompose it further
|
||||
{
|
||||
goalsToAdd.push_back(elementarGoal);
|
||||
//decompose further now - this is necesssary if we can't add over 10 goals in the pool
|
||||
goalToDecompose = elementarGoal;
|
||||
//there is a risk of infinite abstract goal loop, though it indicates failed logic
|
||||
maxAbstractGoals--;
|
||||
}
|
||||
else if (elementarGoal->isElementar) //should be
|
||||
{
|
||||
logAi->debug("Found elementar goal %s", elementarGoal->name());
|
||||
ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal?
|
||||
break;
|
||||
}
|
||||
else //should never be here
|
||||
throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name());
|
||||
}
|
||||
|
||||
//realize best goal
|
||||
if (!elementarGoal->invalid())
|
||||
{
|
||||
logAi->debug("Trying to realize %s (value %2.3f)", elementarGoal->name(), elementarGoal->priority);
|
||||
|
||||
try
|
||||
{
|
||||
boost::this_thread::interruption_point();
|
||||
elementarGoal->accept(this); //visitor pattern
|
||||
boost::this_thread::interruption_point();
|
||||
}
|
||||
catch (boost::thread_interrupted & e)
|
||||
{
|
||||
logAi->debug("Player %d: Making turn thread received an interruption!", playerID);
|
||||
throw; //rethrow, we want to truly end this thread
|
||||
}
|
||||
catch (goalFulfilledException & e)
|
||||
{
|
||||
//the sub-goal was completed successfully
|
||||
completeGoal(e.goal);
|
||||
//local goal was also completed
|
||||
completeGoal(elementarGoal);
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
logAi->debug("Failed to realize subgoal of type %s, I will stop.", elementarGoal->name());
|
||||
logAi->debug("The error message was: %s", e.what());
|
||||
|
||||
//erase base goal if we failed to execute decomposed goal
|
||||
for (auto basicGoalToRemove : ultimateGoalsFromBasic[elementarGoal])
|
||||
goalsToRemove.push_back(basicGoalToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Goals::TSubgoal VCAI::decomposeGoal(Goals::TSubgoal ultimateGoal)
|
||||
{
|
||||
if(ultimateGoal->isElementar)
|
||||
{
|
||||
logAi->warn("Trying to decompose elementar goal %s", ultimateGoal->name());
|
||||
|
||||
return ultimateGoal;
|
||||
}
|
||||
|
||||
const int searchDepth = 30;
|
||||
|
||||
Goals::TSubgoal goal = ultimateGoal;
|
||||
logAi->debug("Decomposing goal %s", ultimateGoal->name());
|
||||
int maxGoals = searchDepth; //preventing deadlock for mutually dependent goals
|
||||
while (maxGoals)
|
||||
{
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
goal = goal->whatToDoToAchieve(); //may throw if decomposition fails
|
||||
--maxGoals;
|
||||
if (goal == ultimateGoal) //compare objects by value
|
||||
if (goal->isElementar == ultimateGoal->isElementar)
|
||||
throw cannotFulfillGoalException((boost::format("Goal dependency loop detected for %s!")
|
||||
% ultimateGoal->name()).str());
|
||||
if (goal->isAbstract || goal->isElementar)
|
||||
return goal;
|
||||
else
|
||||
logAi->debug("Considering: %s", goal->name());
|
||||
}
|
||||
|
||||
throw cannotFulfillGoalException("Too many subgoals, don't know what to do");
|
||||
}
|
||||
|
||||
void VCAI::performTypicalActions()
|
||||
{
|
||||
for(auto h : getUnblockedHeroes())
|
||||
{
|
||||
if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
|
||||
continue;
|
||||
|
||||
logAi->debug("Hero %s started wandering, MP=%d", h->name.c_str(), h->movement);
|
||||
makePossibleUpgrades(*h);
|
||||
pickBestArtifacts(*h);
|
||||
try
|
||||
{
|
||||
wander(h);
|
||||
}
|
||||
catch(std::exception & e)
|
||||
{
|
||||
logAi->debug("Cannot use this hero anymore, received exception: %s", e.what());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::buildArmyIn(const CGTownInstance * t)
|
||||
{
|
||||
makePossibleUpgrades(t->visitingHero);
|
||||
@ -2507,16 +1856,6 @@ void VCAI::buildArmyIn(const CGTownInstance * t)
|
||||
moveCreaturesToHero(t);
|
||||
}
|
||||
|
||||
void VCAI::checkHeroArmy(HeroPtr h)
|
||||
{
|
||||
auto it = lockedHeroes.find(h);
|
||||
if(it != lockedHeroes.end())
|
||||
{
|
||||
if(it->second->goalType == Goals::GATHER_ARMY && it->second->value <= h->getArmyStrength())
|
||||
completeGoal(sptr(Goals::GatherArmy(it->second->value).sethero(h)));
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
|
||||
{
|
||||
logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
|
||||
@ -2547,6 +1886,8 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
|
||||
|
||||
void VCAI::finish()
|
||||
{
|
||||
//we want to lock to avoid multiple threads from calling makingTurn->join() at same time
|
||||
boost::lock_guard<boost::mutex> multipleCleanupGuard(turnInterruptionMutex);
|
||||
if(makingTurn)
|
||||
{
|
||||
makingTurn->interrupt();
|
||||
@ -2570,41 +1911,11 @@ void VCAI::lostHero(HeroPtr h)
|
||||
{
|
||||
logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name);
|
||||
|
||||
vstd::erase_if_present(lockedHeroes, h);
|
||||
for(auto obj : reservedHeroesMap[h])
|
||||
{
|
||||
vstd::erase_if_present(reservedObjs, obj); //unreserve all objects for that hero
|
||||
}
|
||||
vstd::erase_if_present(reservedHeroesMap, h);
|
||||
vstd::erase_if_present(visitedHeroes, h);
|
||||
for (auto heroVec : visitedHeroes)
|
||||
{
|
||||
vstd::erase_if_present(heroVec.second, h);
|
||||
}
|
||||
|
||||
//remove goals with removed hero assigned from main loop
|
||||
vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool
|
||||
{
|
||||
if(x.first->hero == h)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
auto removedHeroGoalPredicate = [&](const Goals::TSubgoal & x) ->bool
|
||||
{
|
||||
if(x->hero == h)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
};
|
||||
|
||||
vstd::erase_if(basicGoals, removedHeroGoalPredicate);
|
||||
vstd::erase_if(goalsToAdd, removedHeroGoalPredicate);
|
||||
vstd::erase_if(goalsToRemove, removedHeroGoalPredicate);
|
||||
|
||||
for(auto goal : ultimateGoalsFromBasic)
|
||||
vstd::erase_if(goal.second, removedHeroGoalPredicate);
|
||||
}
|
||||
|
||||
void VCAI::answerQuery(QueryID queryID, int selection)
|
||||
@ -2914,7 +2225,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||
case Obj::SCHOOL_OF_MAGIC:
|
||||
case Obj::SCHOOL_OF_WAR:
|
||||
{
|
||||
if (ai->ah->freeGold() < 1000)
|
||||
if (cb->getResourceAmount(Res::GOLD) < 1000)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -2924,7 +2235,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||
break;
|
||||
case Obj::TREE_OF_KNOWLEDGE:
|
||||
{
|
||||
TResources myRes = ai->ah->freeResources();
|
||||
TResources myRes = cb->getResourceAmount();
|
||||
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
|
||||
return false;
|
||||
break;
|
||||
@ -2939,7 +2250,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||
//TODO: only on request
|
||||
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
|
||||
return false;
|
||||
else if(ai->ah->freeGold() < GameConstants::HERO_GOLD_COST)
|
||||
else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
@ -13,9 +13,7 @@
|
||||
#include "Goals/AbstractGoal.h"
|
||||
#include "../../lib/AI_Base.h"
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/CThreadHelper.h"
|
||||
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
@ -90,12 +88,6 @@ public:
|
||||
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
|
||||
std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
|
||||
|
||||
//part of mainLoop, but accessible from outisde
|
||||
std::vector<Goals::TSubgoal> basicGoals;
|
||||
Goals::TGoalVec goalsToRemove;
|
||||
Goals::TGoalVec goalsToAdd;
|
||||
std::map<Goals::TSubgoal, Goals::TGoalVec> ultimateGoalsFromBasic; //theoreticlaly same goal can fulfill multiple basic goals
|
||||
|
||||
std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
|
||||
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
|
||||
std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
|
||||
@ -113,6 +105,9 @@ public:
|
||||
std::shared_ptr<CCallback> myCb;
|
||||
|
||||
std::unique_ptr<boost::thread> makingTurn;
|
||||
private:
|
||||
boost::mutex turnInterruptionMutex;
|
||||
public:
|
||||
ObjectInstanceID selectedObject;
|
||||
|
||||
AIhelper * ah;
|
||||
@ -199,17 +194,9 @@ public:
|
||||
void battleEnd(const BattleResult * br) override;
|
||||
|
||||
void makeTurn();
|
||||
void mainLoop();
|
||||
void performTypicalActions();
|
||||
|
||||
void buildArmyIn(const CGTownInstance * t);
|
||||
void striveToGoal(Goals::TSubgoal ultimateGoal);
|
||||
Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal);
|
||||
void endTurn();
|
||||
void wander(HeroPtr h);
|
||||
void setGoal(HeroPtr h, Goals::TSubgoal goal);
|
||||
void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
|
||||
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
|
||||
|
||||
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
|
||||
@ -230,8 +217,6 @@ public:
|
||||
|
||||
void addVisitableObj(const CGObjectInstance * obj);
|
||||
void markObjectVisited(const CGObjectInstance * obj);
|
||||
void reserveObject(HeroPtr h, const CGObjectInstance * obj); //TODO: reserve all objects that heroes attempt to visit
|
||||
void unreserveObject(HeroPtr h, const CGObjectInstance * obj);
|
||||
|
||||
void markHeroUnableToExplore(HeroPtr h);
|
||||
void markHeroAbleToExplore(HeroPtr h);
|
||||
@ -261,7 +246,6 @@ public:
|
||||
std::vector<HeroPtr> getUnblockedHeroes() const;
|
||||
std::vector<HeroPtr> getMyHeroes() const;
|
||||
HeroPtr primaryHero() const;
|
||||
void checkHeroArmy(HeroPtr h);
|
||||
|
||||
void requestSent(const CPackForServer * pack, int requestID) override;
|
||||
void answerQuery(QueryID queryID, int selection);
|
||||
|
@ -21,7 +21,7 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{276C3DB0-7A6B-4417-8E5C-322B08633AAC}</ProjectGuid>
|
||||
<RootNamespace>StupidAI</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@ -41,7 +41,7 @@
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
@ -117,7 +117,7 @@
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
Loading…
Reference in New Issue
Block a user