mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Nullkiller: initial cleanup of unused code
This commit is contained in:
committed by
Andrii Danylchenko
parent
5344df51c6
commit
af0dcf97c4
@@ -13,8 +13,6 @@
|
|||||||
|
|
||||||
AIhelper::AIhelper()
|
AIhelper::AIhelper()
|
||||||
{
|
{
|
||||||
resourceManager.reset(new ResourceManager());
|
|
||||||
buildingManager.reset(new BuildingManager());
|
|
||||||
pathfindingManager.reset(new PathfindingManager());
|
pathfindingManager.reset(new PathfindingManager());
|
||||||
armyManager.reset(new ArmyManager());
|
armyManager.reset(new ArmyManager());
|
||||||
heroManager.reset(new HeroManager());
|
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)
|
void AIhelper::init(CPlayerSpecificInfoCallback * CB)
|
||||||
{
|
{
|
||||||
resourceManager->init(CB);
|
|
||||||
buildingManager->init(CB);
|
|
||||||
pathfindingManager->init(CB);
|
pathfindingManager->init(CB);
|
||||||
armyManager->init(CB);
|
armyManager->init(CB);
|
||||||
heroManager->init(CB);
|
heroManager->init(CB);
|
||||||
@@ -40,8 +31,6 @@ void AIhelper::init(CPlayerSpecificInfoCallback * CB)
|
|||||||
|
|
||||||
void AIhelper::setAI(VCAI * AI)
|
void AIhelper::setAI(VCAI * AI)
|
||||||
{
|
{
|
||||||
resourceManager->setAI(AI);
|
|
||||||
buildingManager->setAI(AI);
|
|
||||||
pathfindingManager->setAI(AI);
|
pathfindingManager->setAI(AI);
|
||||||
armyManager->setAI(AI);
|
armyManager->setAI(AI);
|
||||||
heroManager->setAI(AI);
|
heroManager->setAI(AI);
|
||||||
@@ -53,106 +42,6 @@ void AIhelper::update()
|
|||||||
heroManager->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
|
std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
|
||||||
{
|
{
|
||||||
return pathfindingManager->getPathsToTile(hero, tile);
|
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
|
!!! 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 "ArmyManager.h"
|
||||||
#include "HeroManager.h"
|
#include "HeroManager.h"
|
||||||
#include "Pathfinding/PathfindingManager.h"
|
#include "Pathfinding/PathfindingManager.h"
|
||||||
@@ -24,14 +22,11 @@ class ResourceManager;
|
|||||||
class BuildingManager;
|
class BuildingManager;
|
||||||
|
|
||||||
|
|
||||||
//indirection interface for various modules
|
//TODO: remove class, put managers to engine
|
||||||
class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager, public IArmyManager, public IHeroManager
|
class DLL_EXPORT AIhelper : public IPathfindingManager, public IArmyManager, public IHeroManager
|
||||||
{
|
{
|
||||||
friend class VCAI;
|
//std::shared_ptr<ResourceManager> resourceManager;
|
||||||
friend struct SetGlobalState; //mess?
|
//std::shared_ptr<BuildingManager> buildingManager;
|
||||||
|
|
||||||
std::shared_ptr<ResourceManager> resourceManager;
|
|
||||||
std::shared_ptr<BuildingManager> buildingManager;
|
|
||||||
std::shared_ptr<PathfindingManager> pathfindingManager;
|
std::shared_ptr<PathfindingManager> pathfindingManager;
|
||||||
std::shared_ptr<ArmyManager> armyManager;
|
std::shared_ptr<ArmyManager> armyManager;
|
||||||
std::shared_ptr<HeroManager> heroManager;
|
std::shared_ptr<HeroManager> heroManager;
|
||||||
@@ -40,29 +35,6 @@ public:
|
|||||||
AIhelper();
|
AIhelper();
|
||||||
~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 HeroPtr & hero, const int3 & tile) const override;
|
||||||
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
|
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
|
||||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||||
@@ -95,9 +67,6 @@ public:
|
|||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
private:
|
|
||||||
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
|
|
||||||
|
|
||||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||||
void setAI(VCAI * AI) 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)
|
if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if(path.specialAction && !path.specialAction->canAct(path.targetHero))
|
||||||
|
{
|
||||||
|
auto subGoal = path.specialAction->whatToDo(path.targetHero);
|
||||||
|
}
|
||||||
|
|
||||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||||
|
|
||||||
#ifdef AI_TRACE_LEVEL >= 2
|
#ifdef AI_TRACE_LEVEL >= 2
|
||||||
|
@@ -148,7 +148,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
if(treatIsUnderControl)
|
if(treatIsUnderControl)
|
||||||
continue;
|
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);
|
auto heroesInTavern = cb->getAvailableHeroes(town);
|
||||||
|
|
||||||
|
@@ -56,6 +56,8 @@ Goals::TGoalVec GatherArmyBehavior::getTasks()
|
|||||||
{
|
{
|
||||||
vstd::concatenate(tasks, upgradeArmy(town));
|
vstd::concatenate(tasks, upgradeArmy(town));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * hero) const
|
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
|
AIhelper.cpp
|
||||||
ArmyManager.cpp
|
ArmyManager.cpp
|
||||||
HeroManager.cpp
|
HeroManager.cpp
|
||||||
ResourceManager.cpp
|
|
||||||
BuildingManager.cpp
|
|
||||||
SectorMap.cpp
|
|
||||||
BuildingManager.cpp
|
|
||||||
MapObjectsEvaluator.cpp
|
MapObjectsEvaluator.cpp
|
||||||
FuzzyEngines.cpp
|
FuzzyEngines.cpp
|
||||||
FuzzyHelper.cpp
|
FuzzyHelper.cpp
|
||||||
Goals/AbstractGoal.cpp
|
Goals/AbstractGoal.cpp
|
||||||
Goals/BuildBoat.cpp
|
Goals/BuildBoat.cpp
|
||||||
Goals/Build.cpp
|
|
||||||
Goals/BuildThis.cpp
|
Goals/BuildThis.cpp
|
||||||
Goals/Explore.cpp
|
|
||||||
Goals/GatherArmy.cpp
|
|
||||||
Goals/DismissHero.cpp
|
Goals/DismissHero.cpp
|
||||||
Goals/GatherTroops.cpp
|
Goals/GatherTroops.cpp
|
||||||
Goals/BuyArmy.cpp
|
Goals/BuyArmy.cpp
|
||||||
@@ -41,8 +34,6 @@ set(VCAI_SRCS
|
|||||||
Goals/CollectRes.cpp
|
Goals/CollectRes.cpp
|
||||||
Goals/Trade.cpp
|
Goals/Trade.cpp
|
||||||
Goals/RecruitHero.cpp
|
Goals/RecruitHero.cpp
|
||||||
Goals/Conquer.cpp
|
|
||||||
Goals/ClearWayTo.cpp
|
|
||||||
Goals/DigAtTile.cpp
|
Goals/DigAtTile.cpp
|
||||||
Goals/GetArtOfType.cpp
|
Goals/GetArtOfType.cpp
|
||||||
Goals/FindObj.cpp
|
Goals/FindObj.cpp
|
||||||
@@ -85,10 +76,6 @@ set(VCAI_HEADERS
|
|||||||
AIhelper.h
|
AIhelper.h
|
||||||
ArmyManager.h
|
ArmyManager.h
|
||||||
HeroManager.h
|
HeroManager.h
|
||||||
ResourceManager.h
|
|
||||||
BuildingManager.h
|
|
||||||
SectorMap.h
|
|
||||||
BuildingManager.h
|
|
||||||
MapObjectsEvaluator.h
|
MapObjectsEvaluator.h
|
||||||
FuzzyEngines.h
|
FuzzyEngines.h
|
||||||
FuzzyHelper.h
|
FuzzyHelper.h
|
||||||
@@ -96,10 +83,7 @@ set(VCAI_HEADERS
|
|||||||
Goals/CGoal.h
|
Goals/CGoal.h
|
||||||
Goals/Invalid.h
|
Goals/Invalid.h
|
||||||
Goals/BuildBoat.h
|
Goals/BuildBoat.h
|
||||||
Goals/Build.h
|
|
||||||
Goals/BuildThis.h
|
Goals/BuildThis.h
|
||||||
Goals/Explore.h
|
|
||||||
Goals/GatherArmy.h
|
|
||||||
Goals/DismissHero.h
|
Goals/DismissHero.h
|
||||||
Goals/GatherTroops.h
|
Goals/GatherTroops.h
|
||||||
Goals/BuyArmy.h
|
Goals/BuyArmy.h
|
||||||
@@ -111,8 +95,6 @@ set(VCAI_HEADERS
|
|||||||
Goals/CollectRes.h
|
Goals/CollectRes.h
|
||||||
Goals/Trade.h
|
Goals/Trade.h
|
||||||
Goals/RecruitHero.h
|
Goals/RecruitHero.h
|
||||||
Goals/Conquer.h
|
|
||||||
Goals/ClearWayTo.h
|
|
||||||
Goals/DigAtTile.h
|
Goals/DigAtTile.h
|
||||||
Goals/GetArtOfType.h
|
Goals/GetArtOfType.h
|
||||||
Goals/FindObj.h
|
Goals/FindObj.h
|
||||||
|
@@ -184,7 +184,7 @@ void Nullkiller::makeTurn()
|
|||||||
}
|
}
|
||||||
catch(goalFulfilledException &)
|
catch(goalFulfilledException &)
|
||||||
{
|
{
|
||||||
logAi->trace(bestTask->completeMessage());
|
logAi->trace("Task %s completed", bestTask->name());
|
||||||
}
|
}
|
||||||
catch(std::exception & e)
|
catch(std::exception & e)
|
||||||
{
|
{
|
||||||
|
@@ -23,7 +23,8 @@ extern FuzzyHelper * fh;
|
|||||||
|
|
||||||
engineBase::engineBase()
|
engineBase::engineBase()
|
||||||
{
|
{
|
||||||
engine.addRuleBlock(&rules);
|
rules = new fl::RuleBlock();
|
||||||
|
engine.addRuleBlock(rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
void engineBase::configure()
|
void engineBase::configure()
|
||||||
@@ -34,7 +35,7 @@ void engineBase::configure()
|
|||||||
|
|
||||||
void engineBase::addRule(const std::string & txt)
|
void engineBase::addRule(const std::string & txt)
|
||||||
{
|
{
|
||||||
rules.addRule(fl::Rule::parse(txt, &engine));
|
rules->addRule(fl::Rule::parse(txt, &engine));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct armyStructure
|
struct armyStructure
|
||||||
|
@@ -17,7 +17,7 @@ class engineBase //subclasses create fuzzylite variables with "new" that are not
|
|||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
fl::Engine engine;
|
fl::Engine engine;
|
||||||
fl::RuleBlock rules;
|
fl::RuleBlock * rules;
|
||||||
virtual void configure();
|
virtual void configure();
|
||||||
void addRule(const std::string & txt);
|
void addRule(const std::string & txt);
|
||||||
public:
|
public:
|
||||||
|
@@ -19,44 +19,6 @@ FuzzyHelper * fh;
|
|||||||
extern boost::thread_specific_ptr<VCAI> ai;
|
extern boost::thread_specific_ptr<VCAI> ai;
|
||||||
extern boost::thread_specific_ptr<CCallback> cb;
|
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)
|
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)
|
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
|
||||||
{
|
{
|
||||||
return evaluateDanger(tile, visitor, ai.get());
|
return evaluateDanger(tile, visitor, ai.get());
|
||||||
|
@@ -16,33 +16,9 @@ class DLL_EXPORT FuzzyHelper
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TacticalAdvantageEngine tacticalAdvantageEngine;
|
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?
|
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(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, const VCAI * ai, bool checkGuards = true);
|
||||||
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
|
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
|
||||||
|
@@ -12,8 +12,6 @@
|
|||||||
#include "../VCAI.h"
|
#include "../VCAI.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.h"
|
#include "../../../lib/StringConstants.h"
|
||||||
@@ -115,63 +113,12 @@ bool AbstractGoal::operator==(const AbstractGoal & g) const
|
|||||||
return false;
|
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?
|
//TODO: find out why the following are not generated automatically on MVS?
|
||||||
bool TSubgoal::operator==(const TSubgoal & rhs) const
|
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
|
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
|
bool AbstractGoal::invalid() const
|
||||||
{
|
{
|
||||||
return goalType == EGoals::INVALID;
|
return goalType == EGoals::INVALID;
|
||||||
@@ -182,11 +129,6 @@ void AbstractGoal::accept(VCAI * ai)
|
|||||||
ai->tryRealize(*this);
|
ai->tryRealize(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
float AbstractGoal::accept(FuzzyHelper * f)
|
|
||||||
{
|
|
||||||
return f->evaluate(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
EvaluationContext::EvaluationContext()
|
EvaluationContext::EvaluationContext()
|
||||||
: movementCost(0.0),
|
: movementCost(0.0),
|
||||||
manaCost(0),
|
manaCost(0),
|
||||||
|
@@ -30,7 +30,6 @@ namespace Goals
|
|||||||
class BuildThis;
|
class BuildThis;
|
||||||
class DigAtTile;
|
class DigAtTile;
|
||||||
class CollectRes;
|
class CollectRes;
|
||||||
class Build;
|
|
||||||
class BuyArmy;
|
class BuyArmy;
|
||||||
class BuildBoat;
|
class BuildBoat;
|
||||||
class GatherArmy;
|
class GatherArmy;
|
||||||
@@ -43,7 +42,8 @@ namespace Goals
|
|||||||
enum EGoals
|
enum EGoals
|
||||||
{
|
{
|
||||||
INVALID = -1,
|
INVALID = -1,
|
||||||
WIN, CONQUER, BUILD, //build needs to get a real reasoning
|
WIN, CONQUER,
|
||||||
|
BUILD,
|
||||||
EXPLORE, GATHER_ARMY,
|
EXPLORE, GATHER_ARMY,
|
||||||
BOOST_HERO,
|
BOOST_HERO,
|
||||||
RECRUIT_HERO,
|
RECRUIT_HERO,
|
||||||
@@ -162,25 +162,15 @@ namespace Goals
|
|||||||
EGoals goalType;
|
EGoals goalType;
|
||||||
|
|
||||||
virtual std::string name() const;
|
virtual std::string name() const;
|
||||||
virtual std::string completeMessage() const
|
|
||||||
{
|
|
||||||
return "This goal is unspecified!";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool invalid() const;
|
bool invalid() const;
|
||||||
|
|
||||||
///Visitor pattern
|
///Visitor pattern
|
||||||
//TODO: make accept work for std::shared_ptr... somehow
|
//TODO: make accept work for std::shared_ptr... somehow
|
||||||
virtual void accept(VCAI * ai); //unhandled goal will report standard error
|
virtual void accept(VCAI * ai); //unhandled goal will report standard error
|
||||||
virtual float accept(FuzzyHelper * f);
|
|
||||||
|
|
||||||
virtual bool operator==(const AbstractGoal & g) const;
|
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
|
bool operator!=(const AbstractGoal & g) const
|
||||||
{
|
{
|
||||||
return !(*this == g);
|
return !(*this == g);
|
||||||
|
@@ -77,8 +77,3 @@ std::string AdventureSpellCast::name() const
|
|||||||
{
|
{
|
||||||
return "AdventureSpellCast " + spellID.toSpell()->name;
|
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;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
void accept(VCAI * ai) override;
|
void accept(VCAI * ai) override;
|
||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const AdventureSpellCast & other) 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;
|
return shipyard->o->id == other.shipyard->o->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
TSubgoal BuildBoat::whatToDoToAchieve()
|
//TSubgoal BuildBoat::whatToDoToAchieve()
|
||||||
{
|
//{
|
||||||
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
// if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
||||||
{
|
// {
|
||||||
return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o));
|
// return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o));
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if(shipyard->shipyardStatus() != IShipyard::GOOD)
|
// if(shipyard->shipyardStatus() != IShipyard::GOOD)
|
||||||
{
|
// {
|
||||||
throw cannotFulfillGoalException("Shipyard is busy.");
|
// throw cannotFulfillGoalException("Shipyard is busy.");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
TResources boatCost;
|
// TResources boatCost;
|
||||||
shipyard->getBoatCost(boatCost);
|
// shipyard->getBoatCost(boatCost);
|
||||||
|
//
|
||||||
return ai->ah->whatToDo(boatCost, this->iAmElementar());
|
// return ai->ah->whatToDo(boatCost, this->iAmElementar());
|
||||||
}
|
//}
|
||||||
|
|
||||||
void BuildBoat::accept(VCAI * ai)
|
void BuildBoat::accept(VCAI * ai)
|
||||||
{
|
{
|
||||||
@@ -79,8 +79,3 @@ std::string BuildBoat::name() const
|
|||||||
{
|
{
|
||||||
return "BuildBoat";
|
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();
|
return TGoalVec();
|
||||||
}
|
}
|
||||||
TSubgoal whatToDoToAchieve() override;
|
|
||||||
void accept(VCAI * ai) override;
|
void accept(VCAI * ai) override;
|
||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const BuildBoat & other) const override;
|
virtual bool operator==(const BuildBoat & other) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,6 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.h"
|
#include "../../../lib/StringConstants.h"
|
||||||
@@ -34,46 +32,4 @@ bool BuildThis::operator==(const BuildThis & other) const
|
|||||||
std::string BuildThis::name() const
|
std::string BuildThis::name() const
|
||||||
{
|
{
|
||||||
return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name;
|
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;
|
bid = Bid;
|
||||||
priority = 1;
|
priority = 1;
|
||||||
}
|
}
|
||||||
TGoalVec getAllPossibleSubgoals() override
|
|
||||||
{
|
|
||||||
return TGoalVec();
|
|
||||||
}
|
|
||||||
TSubgoal whatToDoToAchieve() override;
|
|
||||||
//bool fulfillsMe(TSubgoal goal) override;
|
|
||||||
virtual bool operator==(const BuildThis & other) const override;
|
virtual bool operator==(const BuildThis & other) const override;
|
||||||
virtual std::string name() 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;
|
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()
|
TSubgoal BuyArmy::whatToDoToAchieve()
|
||||||
{
|
{
|
||||||
//TODO: calculate the actual cost of units instead
|
return iAmElementar();
|
||||||
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;
|
|
||||||
}
|
|
@@ -32,10 +32,8 @@ namespace Goals
|
|||||||
value = val; //expressed in AI unit strength
|
value = val; //expressed in AI unit strength
|
||||||
priority = 3;//TODO: evaluate?
|
priority = 3;//TODO: evaluate?
|
||||||
}
|
}
|
||||||
bool fulfillsMe(TSubgoal goal) override;
|
|
||||||
|
|
||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const BuyArmy & other) 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
|
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
|
CGoal<T> * clone() const override
|
||||||
{
|
{
|
||||||
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
||||||
|
@@ -13,8 +13,6 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.h"
|
#include "../../../lib/StringConstants.h"
|
||||||
@@ -35,80 +33,80 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
|
|||||||
{
|
{
|
||||||
TGoalVec ret;
|
TGoalVec ret;
|
||||||
|
|
||||||
auto givesResource = [this](const CGObjectInstance * obj) -> bool
|
//auto givesResource = [this](const CGObjectInstance * obj) -> bool
|
||||||
{
|
//{
|
||||||
//TODO: move this logic to object side
|
// //TODO: move this logic to object side
|
||||||
//TODO: remember mithril exists
|
// //TODO: remember mithril exists
|
||||||
//TODO: water objects
|
// //TODO: water objects
|
||||||
//TODO: Creature banks
|
// //TODO: Creature banks
|
||||||
|
|
||||||
//return false first from once-visitable, before checking if they were even visited
|
// //return false first from once-visitable, before checking if they were even visited
|
||||||
switch (obj->ID.num)
|
// switch (obj->ID.num)
|
||||||
{
|
// {
|
||||||
case Obj::TREASURE_CHEST:
|
// case Obj::TREASURE_CHEST:
|
||||||
return resID == Res::GOLD;
|
// return resID == Res::GOLD;
|
||||||
break;
|
// break;
|
||||||
case Obj::RESOURCE:
|
// case Obj::RESOURCE:
|
||||||
return obj->subID == resID;
|
// return obj->subID == resID;
|
||||||
break;
|
// break;
|
||||||
case Obj::MINE:
|
// case Obj::MINE:
|
||||||
return (obj->subID == resID &&
|
// return (obj->subID == resID &&
|
||||||
(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
|
// (cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
|
||||||
break;
|
// break;
|
||||||
case Obj::CAMPFIRE:
|
// case Obj::CAMPFIRE:
|
||||||
return true; //contains all resources
|
// return true; //contains all resources
|
||||||
break;
|
// break;
|
||||||
case Obj::WINDMILL:
|
// case Obj::WINDMILL:
|
||||||
switch (resID)
|
// switch (resID)
|
||||||
{
|
// {
|
||||||
case Res::GOLD:
|
// case Res::GOLD:
|
||||||
case Res::WOOD:
|
// case Res::WOOD:
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
case Obj::WATER_WHEEL:
|
// case Obj::WATER_WHEEL:
|
||||||
if (resID != Res::GOLD)
|
// if (resID != Res::GOLD)
|
||||||
return false;
|
// return false;
|
||||||
break;
|
// break;
|
||||||
case Obj::MYSTICAL_GARDEN:
|
// case Obj::MYSTICAL_GARDEN:
|
||||||
if ((resID != Res::GOLD) && (resID != Res::GEMS))
|
// if ((resID != Res::GOLD) && (resID != Res::GEMS))
|
||||||
return false;
|
// return false;
|
||||||
break;
|
// break;
|
||||||
case Obj::LEAN_TO:
|
// case Obj::LEAN_TO:
|
||||||
case Obj::WAGON:
|
// case Obj::WAGON:
|
||||||
if (resID != Res::GOLD)
|
// if (resID != Res::GOLD)
|
||||||
return false;
|
// return false;
|
||||||
break;
|
// break;
|
||||||
default:
|
// default:
|
||||||
return false;
|
// return false;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
|
// return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
|
||||||
};
|
//};
|
||||||
|
|
||||||
std::vector<const CGObjectInstance *> objs;
|
//std::vector<const CGObjectInstance *> objs;
|
||||||
for (auto obj : ai->visitableObjs)
|
//for (auto obj : ai->visitableObjs)
|
||||||
{
|
//{
|
||||||
if (givesResource(obj))
|
// if (givesResource(obj))
|
||||||
objs.push_back(obj);
|
// objs.push_back(obj);
|
||||||
}
|
//}
|
||||||
for (auto h : cb->getHeroesInfo())
|
//for (auto h : cb->getHeroesInfo())
|
||||||
{
|
//{
|
||||||
std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
|
// std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
|
||||||
|
|
||||||
for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
|
// for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
|
||||||
{
|
// {
|
||||||
if (givesResource(obj))
|
// if (givesResource(obj))
|
||||||
ourObjs.push_back(obj);
|
// ourObjs.push_back(obj);
|
||||||
}
|
// }
|
||||||
|
|
||||||
for (auto obj : ourObjs)
|
// for (auto obj : ourObjs)
|
||||||
{
|
// {
|
||||||
auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
|
// auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
|
||||||
|
|
||||||
vstd::concatenate(ret, waysToGo);
|
// vstd::concatenate(ret, waysToGo);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,90 +117,78 @@ TSubgoal CollectRes::whatToDoToAchieve()
|
|||||||
if (!trade->invalid())
|
if (!trade->invalid())
|
||||||
goals.push_back(trade);
|
goals.push_back(trade);
|
||||||
|
|
||||||
if (goals.empty())
|
return sptr(Invalid()); //we can always do that
|
||||||
return sptr(Explore()); //we can always do that
|
|
||||||
else
|
|
||||||
return fh->chooseSolution(goals); //TODO: evaluate trading
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TSubgoal CollectRes::whatToDoToTrade()
|
TSubgoal CollectRes::whatToDoToTrade()
|
||||||
{
|
{
|
||||||
std::vector<const IMarket *> markets;
|
//std::vector<const IMarket *> markets;
|
||||||
|
|
||||||
std::vector<const CGObjectInstance *> visObjs;
|
//std::vector<const CGObjectInstance *> visObjs;
|
||||||
ai->retrieveVisitableObjs(visObjs, true);
|
//ai->retrieveVisitableObjs(visObjs, true);
|
||||||
for (const CGObjectInstance * obj : visObjs)
|
//for (const CGObjectInstance * obj : visObjs)
|
||||||
{
|
//{
|
||||||
if (const IMarket * m = IMarket::castFrom(obj, false))
|
// if (const IMarket * m = IMarket::castFrom(obj, false))
|
||||||
{
|
// {
|
||||||
if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
|
// if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
|
||||||
markets.push_back(m);
|
// markets.push_back(m);
|
||||||
else if (obj->ID == Obj::TRADING_POST)
|
// else if (obj->ID == Obj::TRADING_POST)
|
||||||
markets.push_back(m);
|
// markets.push_back(m);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
|
//boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
|
||||||
{
|
//{
|
||||||
return m1->getMarketEfficiency() < m2->getMarketEfficiency();
|
// return m1->getMarketEfficiency() < m2->getMarketEfficiency();
|
||||||
});
|
//});
|
||||||
|
|
||||||
markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
|
//markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
|
||||||
{
|
//{
|
||||||
if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
|
// if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
|
||||||
{
|
// {
|
||||||
if (!ai->isAccessible(market->o->visitablePos()))
|
// if (!ai->isAccessible(market->o->visitablePos()))
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
return false;
|
// return false;
|
||||||
}), markets.end());
|
//}), markets.end());
|
||||||
|
|
||||||
if (!markets.size())
|
//if (!markets.size())
|
||||||
{
|
//{
|
||||||
for (const CGTownInstance * t : cb->getTownsInfo())
|
// for (const CGTownInstance * t : cb->getTownsInfo())
|
||||||
{
|
// {
|
||||||
if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
|
// if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
|
||||||
return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
|
// return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
else
|
//else
|
||||||
{
|
//{
|
||||||
const IMarket * m = markets.back();
|
// const IMarket * m = markets.back();
|
||||||
//attempt trade at back (best prices)
|
// //attempt trade at back (best prices)
|
||||||
int howManyCanWeBuy = 0;
|
// int howManyCanWeBuy = 0;
|
||||||
for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
// for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
||||||
{
|
// {
|
||||||
if (i == resID)
|
// if (i == resID)
|
||||||
continue;
|
// continue;
|
||||||
int toGive = -1, toReceive = -1;
|
// int toGive = -1, toReceive = -1;
|
||||||
m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
|
// m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
|
||||||
assert(toGive > 0 && toReceive > 0);
|
// assert(toGive > 0 && toReceive > 0);
|
||||||
howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
|
// howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (howManyCanWeBuy >= value)
|
// if (howManyCanWeBuy >= value)
|
||||||
{
|
// {
|
||||||
auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
// auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
||||||
assert(backObj);
|
// assert(backObj);
|
||||||
auto objid = m->o->id.getNum();
|
// auto objid = m->o->id.getNum();
|
||||||
if (backObj->tempOwner != ai->playerID) //top object not owned
|
// if (backObj->tempOwner != ai->playerID) //top object not owned
|
||||||
{
|
// {
|
||||||
return sptr(VisitObj(objid)); //just go there
|
// return sptr(VisitObj(objid)); //just go there
|
||||||
}
|
// }
|
||||||
else //either it's our town, or we have hero 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(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
return sptr(Invalid()); //cannot trade
|
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;
|
TGoalVec getAllPossibleSubgoals() override;
|
||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
TSubgoal whatToDoToTrade();
|
TSubgoal whatToDoToTrade();
|
||||||
bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
|
|
||||||
virtual bool operator==(const CollectRes & other) const override;
|
virtual bool operator==(const CollectRes & other) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -82,9 +82,8 @@ TSubgoal CompleteQuest::whatToDoToAchieve()
|
|||||||
|
|
||||||
TGoalVec solutions = getAllPossibleSubgoals();
|
TGoalVec solutions = getAllPossibleSubgoals();
|
||||||
|
|
||||||
if(solutions.empty())
|
throw cannotFulfillGoalException("Can not complete quest " + questToString());
|
||||||
throw cannotFulfillGoalException("Can not complete quest " + questToString());
|
/*
|
||||||
|
|
||||||
TSubgoal result = fh->chooseSolution(solutions);
|
TSubgoal result = fh->chooseSolution(solutions);
|
||||||
|
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
@@ -94,7 +93,7 @@ TSubgoal CompleteQuest::whatToDoToAchieve()
|
|||||||
result->objid,
|
result->objid,
|
||||||
result->hero.validAndSet() ? result->hero->name : "not specified");
|
result->hero.validAndSet() ? result->hero->name : "not specified");
|
||||||
|
|
||||||
return result;
|
return result;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CompleteQuest::name() const
|
std::string CompleteQuest::name() const
|
||||||
@@ -102,11 +101,6 @@ std::string CompleteQuest::name() const
|
|||||||
return "CompleteQuest";
|
return "CompleteQuest";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CompleteQuest::completeMessage() const
|
|
||||||
{
|
|
||||||
return "Completed quest " + questToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CompleteQuest::questToString() const
|
std::string CompleteQuest::questToString() const
|
||||||
{
|
{
|
||||||
if(q.quest->missionType == CQuest::MISSION_NONE)
|
if(q.quest->missionType == CQuest::MISSION_NONE)
|
||||||
@@ -128,7 +122,7 @@ TGoalVec CompleteQuest::tryCompleteQuest() const
|
|||||||
{
|
{
|
||||||
if(q.quest->checkQuest(hero))
|
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
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -254,7 +248,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const
|
|||||||
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
|
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
|
||||||
|
|
||||||
if(!obj)
|
if(!obj)
|
||||||
return ai->ah->howToVisitObj(q.obj);
|
return solutions;// ai->ah->howToVisitObj(q.obj);
|
||||||
|
|
||||||
if(obj->ID == Obj::HERO)
|
if(obj->ID == Obj::HERO)
|
||||||
{
|
{
|
||||||
@@ -264,11 +258,11 @@ TGoalVec CompleteQuest::missionDestroyObj() const
|
|||||||
{
|
{
|
||||||
auto heroToProtect = cb->getHero(obj->id);
|
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)
|
else if(relations == PlayerRelations::ENEMIES)
|
||||||
{
|
{
|
||||||
solutions = ai->ah->howToVisitObj(obj);
|
//solutions = ai->ah->howToVisitObj(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,7 +28,6 @@ namespace Goals
|
|||||||
TGoalVec getAllPossibleSubgoals() override;
|
TGoalVec getAllPossibleSubgoals() override;
|
||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const CompleteQuest & other) const override;
|
virtual bool operator==(const CompleteQuest & other) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -48,8 +48,3 @@ std::string DismissHero::name() const
|
|||||||
{
|
{
|
||||||
return "DismissHero " + hero.name;
|
return "DismissHero " + hero.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DismissHero::completeMessage() const
|
|
||||||
{
|
|
||||||
return "Hero dismissed successfully " + hero.name;
|
|
||||||
}
|
|
||||||
|
@@ -30,7 +30,6 @@ namespace Goals
|
|||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
void accept(VCAI * ai) override;
|
void accept(VCAI * ai) override;
|
||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const DismissHero & other) 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;
|
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
|
bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
|
||||||
{
|
{
|
||||||
return town == other.town;
|
return town == other.town;
|
||||||
|
@@ -35,7 +35,6 @@ namespace Goals
|
|||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
void accept(VCAI * ai) override;
|
void accept(VCAI * ai) override;
|
||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
|
virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -72,27 +72,29 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
{
|
{
|
||||||
auto & node = chainPath.nodes[i];
|
auto & node = chainPath.nodes[i];
|
||||||
|
|
||||||
HeroPtr hero = node.targetHero;
|
const CGHeroInstance * hero = node.targetHero;
|
||||||
|
HeroPtr heroPtr = hero;
|
||||||
|
|
||||||
if(vstd::contains(blockedIndexes, i))
|
if(vstd::contains(blockedIndexes, i))
|
||||||
{
|
{
|
||||||
blockedIndexes.insert(node.parentIndex);
|
blockedIndexes.insert(node.parentIndex);
|
||||||
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN);
|
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
|
||||||
|
|
||||||
continue;
|
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
|
try
|
||||||
{
|
{
|
||||||
if(hero->movement)
|
if(hero->movement)
|
||||||
{
|
{
|
||||||
ai->nullkiller->setActive(hero.get(), node.coord);
|
|
||||||
|
ai->nullkiller->setActive(hero, node.coord);
|
||||||
|
|
||||||
if(node.specialAction)
|
if(node.specialAction)
|
||||||
{
|
{
|
||||||
if(node.specialAction->canAct(hero.get()))
|
if(node.specialAction->canAct(hero))
|
||||||
{
|
{
|
||||||
auto specialGoal = node.specialAction->whatToDo(hero);
|
auto specialGoal = node.specialAction->whatToDo(hero);
|
||||||
|
|
||||||
@@ -100,12 +102,12 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
}
|
}
|
||||||
else
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -113,7 +115,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
|
|
||||||
if(node.turns == 0 && node.coord != hero->visitablePos())
|
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
|
if(targetNode->accessible == CGPathNode::EAccessibility::NOT_SET
|
||||||
|| targetNode->accessible == CGPathNode::EAccessibility::BLOCKED
|
|| targetNode->accessible == CGPathNode::EAccessibility::BLOCKED
|
||||||
@@ -122,7 +124,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
{
|
{
|
||||||
logAi->error(
|
logAi->error(
|
||||||
"Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this",
|
"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());
|
node.coord.toString());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -137,9 +139,9 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
}
|
}
|
||||||
catch(cannotFulfillGoalException)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -147,13 +149,13 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
if(hero->movement > 0)
|
if(hero->movement > 0)
|
||||||
{
|
{
|
||||||
CGPath path;
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +172,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
{
|
{
|
||||||
logAi->error(
|
logAi->error(
|
||||||
"Enable to complete chain. Expected hero %s to arive to %s but he is at %s",
|
"Enable to complete chain. Expected hero %s to arive to %s but he is at %s",
|
||||||
hero.name,
|
hero->name,
|
||||||
node.coord.toString(),
|
node.coord.toString(),
|
||||||
hero->visitablePos().toString());
|
hero->visitablePos().toString());
|
||||||
|
|
||||||
@@ -178,14 +180,14 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// no exception means we were not able to rich the tile
|
// 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);
|
blockedIndexes.insert(node.parentIndex);
|
||||||
}
|
}
|
||||||
catch(goalFulfilledException)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -196,9 +198,4 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
std::string ExecuteHeroChain::name() const
|
std::string ExecuteHeroChain::name() const
|
||||||
{
|
{
|
||||||
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name;
|
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;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
void accept(VCAI * ai) override;
|
void accept(VCAI * ai) override;
|
||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
||||||
const AIPath & getPath() const { return chainPath; }
|
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;
|
return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Explore::completeMessage() const
|
|
||||||
{
|
|
||||||
return "Hero " + hero.get()->name + " completed exploration";
|
|
||||||
}
|
|
||||||
|
|
||||||
TSubgoal Explore::whatToDoToAchieve()
|
TSubgoal Explore::whatToDoToAchieve()
|
||||||
{
|
{
|
||||||
return fh->chooseSolution(getAllPossibleSubgoals());
|
return sptr(Goals::Invalid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec Explore::getAllPossibleSubgoals()
|
TGoalVec Explore::getAllPossibleSubgoals()
|
||||||
@@ -361,18 +356,6 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
|||||||
return ret;
|
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
|
TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const
|
||||||
{
|
{
|
||||||
ExplorationHelper scanResult(h, allowGatherArmy);
|
ExplorationHelper scanResult(h, allowGatherArmy);
|
||||||
|
@@ -1,66 +1,64 @@
|
|||||||
/*
|
///*
|
||||||
* Explore.h, part of VCMI engine
|
//* Explore.h, part of VCMI engine
|
||||||
*
|
//*
|
||||||
* Authors: listed in file AUTHORS in main folder
|
//* Authors: listed in file AUTHORS in main folder
|
||||||
*
|
//*
|
||||||
* License: GNU General Public License v2.0 or later
|
//* License: GNU General Public License v2.0 or later
|
||||||
* Full text of license available in license.txt file, in main folder
|
//* Full text of license available in license.txt file, in main folder
|
||||||
*
|
//*
|
||||||
*/
|
//*/
|
||||||
#pragma once
|
//#pragma once
|
||||||
|
//
|
||||||
#include "CGoal.h"
|
//#include "CGoal.h"
|
||||||
|
//
|
||||||
struct HeroPtr;
|
//struct HeroPtr;
|
||||||
class VCAI;
|
//class VCAI;
|
||||||
class FuzzyHelper;
|
//class FuzzyHelper;
|
||||||
|
//
|
||||||
namespace Goals
|
//namespace Goals
|
||||||
{
|
//{
|
||||||
struct ExplorationHelper;
|
// struct ExplorationHelper;
|
||||||
|
//
|
||||||
class DLL_EXPORT Explore : public CGoal<Explore>
|
// class DLL_EXPORT Explore : public CGoal<Explore>
|
||||||
{
|
// {
|
||||||
private:
|
// private:
|
||||||
bool allowGatherArmy;
|
// bool allowGatherArmy;
|
||||||
|
//
|
||||||
public:
|
// public:
|
||||||
Explore(bool allowGatherArmy)
|
// Explore(bool allowGatherArmy)
|
||||||
: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
|
// : CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
|
||||||
{
|
// {
|
||||||
priority = 1;
|
// priority = 1;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Explore()
|
// Explore()
|
||||||
: Explore(true)
|
// : Explore(true)
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Explore(HeroPtr h)
|
// Explore(HeroPtr h)
|
||||||
: CGoal(Goals::EXPLORE)
|
// : CGoal(Goals::EXPLORE)
|
||||||
{
|
// {
|
||||||
hero = h;
|
// hero = h;
|
||||||
priority = 1;
|
// priority = 1;
|
||||||
}
|
// }
|
||||||
TGoalVec getAllPossibleSubgoals() override;
|
// TGoalVec getAllPossibleSubgoals() override;
|
||||||
TSubgoal whatToDoToAchieve() override;
|
// TSubgoal whatToDoToAchieve() override;
|
||||||
std::string completeMessage() const override;
|
// virtual bool operator==(const Explore & other) const override;
|
||||||
bool fulfillsMe(TSubgoal goal) override;
|
//
|
||||||
virtual bool operator==(const Explore & other) const override;
|
// private:
|
||||||
|
// TSubgoal exploreNearestNeighbour(HeroPtr h) const;
|
||||||
private:
|
// TSubgoal explorationNewPoint(HeroPtr h) const;
|
||||||
TSubgoal exploreNearestNeighbour(HeroPtr h) const;
|
// TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const;
|
||||||
TSubgoal explorationNewPoint(HeroPtr h) const;
|
// void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const;
|
||||||
TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const;
|
// bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) 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,
|
||||||
void getVisibleNeighbours(
|
// std::vector<int3> & out,
|
||||||
const std::vector<int3> & tiles,
|
// CCallback * cbp,
|
||||||
std::vector<int3> & out,
|
// const TeamState * ts) const;
|
||||||
CCallback * cbp,
|
//
|
||||||
const TeamState * ts) const;
|
// int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const;
|
||||||
|
// };
|
||||||
int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const;
|
//}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@@ -10,7 +10,6 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "FindObj.h"
|
#include "FindObj.h"
|
||||||
#include "VisitObj.h"
|
#include "VisitObj.h"
|
||||||
#include "Explore.h"
|
|
||||||
#include "../VCAI.h"
|
#include "../VCAI.h"
|
||||||
#include "../AIUtility.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
|
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()));
|
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();
|
return TGoalVec();
|
||||||
}
|
}
|
||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
bool fulfillsMe(TSubgoal goal) override;
|
|
||||||
virtual bool operator==(const FindObj & other) const override;
|
virtual bool operator==(const FindObj & other) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -13,8 +13,6 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.h"
|
#include "../../../lib/StringConstants.h"
|
||||||
@@ -62,12 +60,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec solutions = getAllPossibleSubgoals();
|
return sptr(Invalid());
|
||||||
|
|
||||||
if(solutions.empty())
|
|
||||||
return sptr(Explore());
|
|
||||||
|
|
||||||
return fh->chooseSolution(solutions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -75,75 +68,74 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
|
|||||||
{
|
{
|
||||||
TGoalVec solutions;
|
TGoalVec solutions;
|
||||||
|
|
||||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
//for(const CGTownInstance * t : cb->getTownsInfo())
|
||||||
{
|
//{
|
||||||
int count = getCreaturesCount(t->getUpperArmy());
|
// int count = getCreaturesCount(t->getUpperArmy());
|
||||||
|
|
||||||
if(count >= this->value)
|
// if(count >= this->value)
|
||||||
{
|
// {
|
||||||
if(t->visitingHero)
|
// if(t->visitingHero)
|
||||||
{
|
// {
|
||||||
solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get())));
|
// solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get())));
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
|
// vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
|
||||||
}
|
// }
|
||||||
|
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
|
||||||
auto creature = VLC->creh->creatures[objid];
|
// auto creature = VLC->creh->creatures[objid];
|
||||||
if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
|
// 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);
|
// auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
|
||||||
if(!creatures)
|
// if(!creatures)
|
||||||
continue;
|
// continue;
|
||||||
|
|
||||||
int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
|
// int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
|
||||||
if(upgradeNumber < 0)
|
// if(upgradeNumber < 0)
|
||||||
continue;
|
// continue;
|
||||||
|
|
||||||
BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
|
// 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
|
// 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)));
|
// 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
|
// /*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));
|
// return sptr(BuildThis(bid, t).setpriority(priority));
|
||||||
}*/
|
// }*/
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
for(auto obj : ai->visitableObjs)
|
|
||||||
{
|
|
||||||
auto d = dynamic_cast<const CGDwelling *>(obj);
|
|
||||||
|
|
||||||
if(!d || obj->ID == Obj::TOWN)
|
//for(auto obj : ai->visitableObjs)
|
||||||
continue;
|
//{
|
||||||
|
// auto d = dynamic_cast<const CGDwelling *>(obj);
|
||||||
|
|
||||||
for(auto creature : d->creatures)
|
// if(!d || obj->ID == Obj::TOWN)
|
||||||
{
|
// continue;
|
||||||
if(creature.first) //there are more than 0 creatures avaliabe
|
|
||||||
{
|
// for(auto creature : d->creatures)
|
||||||
for(auto type : creature.second)
|
// {
|
||||||
{
|
// if(creature.first) //there are more than 0 creatures avaliabe
|
||||||
if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
|
// {
|
||||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
|
// 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;
|
return solutions;
|
||||||
//TODO: exchange troops between heroes
|
//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;
|
TGoalVec getAllPossibleSubgoals() override;
|
||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
bool fulfillsMe(TSubgoal goal) override;
|
|
||||||
virtual bool operator==(const GatherTroops & other) const override;
|
virtual bool operator==(const GatherTroops & other) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -12,22 +12,17 @@
|
|||||||
#include "CGoal.h"
|
#include "CGoal.h"
|
||||||
#include "Invalid.h"
|
#include "Invalid.h"
|
||||||
#include "BuildBoat.h"
|
#include "BuildBoat.h"
|
||||||
#include "Build.h"
|
|
||||||
#include "BuildThis.h"
|
#include "BuildThis.h"
|
||||||
#include "Conquer.h"
|
|
||||||
#include "GatherArmy.h"
|
|
||||||
#include "Win.h"
|
#include "Win.h"
|
||||||
#include "VisitObj.h"
|
#include "VisitObj.h"
|
||||||
#include "VisitTile.h"
|
#include "VisitTile.h"
|
||||||
#include "VisitHero.h"
|
#include "VisitHero.h"
|
||||||
#include "Explore.h"
|
|
||||||
#include "BuyArmy.h"
|
#include "BuyArmy.h"
|
||||||
#include "GatherTroops.h"
|
#include "GatherTroops.h"
|
||||||
#include "Trade.h"
|
#include "Trade.h"
|
||||||
#include "CollectRes.h"
|
#include "CollectRes.h"
|
||||||
#include "RecruitHero.h"
|
#include "RecruitHero.h"
|
||||||
#include "GetArtOfType.h"
|
#include "GetArtOfType.h"
|
||||||
#include "ClearWayTo.h"
|
|
||||||
#include "DigAtTile.h"
|
#include "DigAtTile.h"
|
||||||
#include "FindObj.h"
|
#include "FindObj.h"
|
||||||
#include "CompleteQuest.h"
|
#include "CompleteQuest.h"
|
||||||
|
@@ -13,8 +13,6 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.h"
|
#include "../../../lib/StringConstants.h"
|
||||||
@@ -25,14 +23,3 @@ extern boost::thread_specific_ptr<VCAI> ai;
|
|||||||
extern FuzzyHelper * fh;
|
extern FuzzyHelper * fh;
|
||||||
|
|
||||||
using namespace Goals;
|
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;
|
priority = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec getAllPossibleSubgoals() override
|
|
||||||
{
|
|
||||||
return TGoalVec();
|
|
||||||
}
|
|
||||||
|
|
||||||
TSubgoal whatToDoToAchieve() override;
|
|
||||||
|
|
||||||
virtual bool operator==(const RecruitHero & other) const override
|
virtual bool operator==(const RecruitHero & other) const override
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@@ -9,14 +9,11 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "VisitHero.h"
|
#include "VisitHero.h"
|
||||||
#include "Explore.h"
|
|
||||||
#include "Invalid.h"
|
#include "Invalid.h"
|
||||||
#include "../VCAI.h"
|
#include "../VCAI.h"
|
||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
|
|
||||||
|
|
||||||
@@ -29,46 +26,4 @@ using namespace Goals;
|
|||||||
bool VisitHero::operator==(const VisitHero & other) const
|
bool VisitHero::operator==(const VisitHero & other) const
|
||||||
{
|
{
|
||||||
return other.hero.h == hero.h && other.objid == objid;
|
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;
|
objid = hid;
|
||||||
priority = 4;
|
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;
|
virtual bool operator==(const VisitHero & other) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,6 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.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;
|
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)
|
VisitObj::VisitObj(int Objid)
|
||||||
: CGoal(VISIT_OBJ)
|
: CGoal(VISIT_OBJ)
|
||||||
{
|
{
|
||||||
@@ -101,18 +40,4 @@ VisitObj::VisitObj(int Objid)
|
|||||||
logAi->error("VisitObj constructed with invalid object instance %d", Objid);
|
logAi->error("VisitObj constructed with invalid object instance %d", Objid);
|
||||||
|
|
||||||
priority = 3;
|
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() = delete; // empty constructor not allowed
|
||||||
VisitObj(int Objid);
|
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;
|
virtual bool operator==(const VisitObj & other) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,6 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.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;
|
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;
|
tile = Tile;
|
||||||
priority = 5;
|
priority = 5;
|
||||||
}
|
}
|
||||||
TGoalVec getAllPossibleSubgoals() override;
|
|
||||||
TSubgoal whatToDoToAchieve() override;
|
|
||||||
std::string completeMessage() const override;
|
|
||||||
virtual bool operator==(const VisitTile & other) const override;
|
virtual bool operator==(const VisitTile & other) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,6 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../ResourceManager.h"
|
|
||||||
#include "../BuildingManager.h"
|
|
||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.h"
|
#include "../../../lib/StringConstants.h"
|
||||||
@@ -62,9 +60,9 @@ TSubgoal Win::whatToDoToAchieve()
|
|||||||
if(goal.object)
|
if(goal.object)
|
||||||
{
|
{
|
||||||
auto obj = cb->getObj(goal.object->id);
|
auto obj = cb->getObj(goal.object->id);
|
||||||
if(obj)
|
/*if(obj)
|
||||||
if(obj->getOwner() == ai->playerID) //we can't capture our own object
|
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()));
|
return sptr(VisitObj(goal.object->id.getNum()));
|
||||||
@@ -122,8 +120,8 @@ TSubgoal Win::whatToDoToAchieve()
|
|||||||
} //TODO: use FIND_OBJ
|
} //TODO: use FIND_OBJ
|
||||||
else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
|
else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
|
||||||
return sptr(VisitObj(obj->id.getNum()));
|
return sptr(VisitObj(obj->id.getNum()));
|
||||||
else
|
/*else
|
||||||
return sptr(Explore());
|
return sptr(Explore());*/
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -166,7 +164,7 @@ TSubgoal Win::whatToDoToAchieve()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EventCondition::STANDARD_WIN:
|
case EventCondition::STANDARD_WIN:
|
||||||
return sptr(Conquer());
|
return sptr(Invalid());
|
||||||
|
|
||||||
// Conditions that likely don't need any implementation
|
// Conditions that likely don't need any implementation
|
||||||
case EventCondition::DAYS_PASSED:
|
case EventCondition::DAYS_PASSED:
|
||||||
@@ -182,7 +180,7 @@ TSubgoal Win::whatToDoToAchieve()
|
|||||||
case EventCondition::HAVE_BUILDING_0:
|
case EventCondition::HAVE_BUILDING_0:
|
||||||
case EventCondition::DESTROY_0:
|
case EventCondition::DESTROY_0:
|
||||||
//TODO: support new condition format
|
//TODO: support new condition format
|
||||||
return sptr(Conquer());
|
return sptr(Invalid());
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
@@ -64,7 +64,12 @@ boost::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance
|
|||||||
auto hero = dynamic_cast<const CGHeroInstance*>(obj);
|
auto hero = dynamic_cast<const CGHeroInstance*>(obj);
|
||||||
return getObjectValue(obj->ID, hero->type->heroClass->id);
|
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);
|
auto dwelling = dynamic_cast<const CGDwelling *>(obj);
|
||||||
int aiValue = 0;
|
int aiValue = 0;
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
namespace AIPathfinding
|
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));
|
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
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const
|
Goals::TSubgoal BuildBoatAction::whatToDo(const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
return Goals::sptr(Goals::BuildBoat(shipyard));
|
return Goals::sptr(Goals::BuildBoat(shipyard));
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ namespace AIPathfinding
|
|||||||
return sourceActor->resourceActor;
|
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));
|
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ namespace AIPathfinding
|
|||||||
class SummonBoatAction : public VirtualBoatAction
|
class SummonBoatAction : public VirtualBoatAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
virtual void applyOnDestination(
|
virtual void applyOnDestination(
|
||||||
const CGHeroInstance * hero,
|
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;
|
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||||
};
|
};
|
||||||
|
@@ -23,7 +23,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
|
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const = 0;
|
||||||
|
|
||||||
virtual void applyOnDestination(
|
virtual void applyOnDestination(
|
||||||
const CGHeroInstance * hero,
|
const CGHeroInstance * hero,
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
using namespace AIPathfinding;
|
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
|
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;
|
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
|
std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
|
||||||
{
|
{
|
||||||
auto paths = pathfinder->getPathInfo(tile);
|
auto paths = pathfinder->getPathInfo(tile);
|
||||||
@@ -118,152 +48,6 @@ std::vector<AIPath> PathfindingManager::getPathsToTile(const int3 & tile) const
|
|||||||
return pathfinder->getPathInfo(tile);
|
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)
|
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
||||||
{
|
{
|
||||||
logAi->debug("AIPathfinder has been reseted.");
|
logAi->debug("AIPathfinder has been reseted.");
|
||||||
|
@@ -21,10 +21,6 @@ public:
|
|||||||
virtual void setAI(VCAI * AI) = 0;
|
virtual void setAI(VCAI * AI) = 0;
|
||||||
|
|
||||||
virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 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 HeroPtr & hero, const int3 & tile) const = 0;
|
||||||
virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0;
|
virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0;
|
||||||
};
|
};
|
||||||
@@ -42,10 +38,6 @@ public:
|
|||||||
PathfindingManager() = default;
|
PathfindingManager() = default;
|
||||||
PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
|
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 HeroPtr & hero, const int3 & tile) const override;
|
||||||
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
|
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
|
||||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||||
|
@@ -26,7 +26,7 @@ namespace AIPathfinding
|
|||||||
return false;
|
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());
|
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-sign-compare" />
|
||||||
<Add option="-Wno-unused-parameter" />
|
<Add option="-Wno-unused-parameter" />
|
||||||
<Add option="-Wno-overloaded-virtual" />
|
<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="-DBOOST_SYSTEM_NO_DEPRECATED" />
|
||||||
<Add option="-D_WIN32_WINNT=0x0501" />
|
<Add option="-D_WIN32_WINNT=0x0501" />
|
||||||
<Add option="-D_WIN32" />
|
<Add option="-D_WIN32" />
|
||||||
@@ -93,6 +93,8 @@
|
|||||||
<Unit filename="AIUtility.h" />
|
<Unit filename="AIUtility.h" />
|
||||||
<Unit filename="AIhelper.cpp" />
|
<Unit filename="AIhelper.cpp" />
|
||||||
<Unit filename="AIhelper.h" />
|
<Unit filename="AIhelper.h" />
|
||||||
|
<Unit filename="ArmyManager.cpp" />
|
||||||
|
<Unit filename="ArmyManager.h" />
|
||||||
<Unit filename="BuildingManager.cpp" />
|
<Unit filename="BuildingManager.cpp" />
|
||||||
<Unit filename="BuildingManager.h" />
|
<Unit filename="BuildingManager.h" />
|
||||||
<Unit filename="FuzzyEngines.cpp" />
|
<Unit filename="FuzzyEngines.cpp" />
|
||||||
|
@@ -10,8 +10,6 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "VCAI.h"
|
#include "VCAI.h"
|
||||||
#include "FuzzyHelper.h"
|
#include "FuzzyHelper.h"
|
||||||
#include "ResourceManager.h"
|
|
||||||
#include "BuildingManager.h"
|
|
||||||
#include "Goals/Goals.h"
|
#include "Goals/Goals.h"
|
||||||
|
|
||||||
#include "../../lib/UnlockGuard.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)
|
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
|
if(start && visitedObj) //we can end visit with null object, anyway
|
||||||
{
|
{
|
||||||
markObjectVisited(visitedObj);
|
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..)
|
//TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..)
|
||||||
if (visitedObj->ID == Obj::HERO)
|
if (visitedObj->ID == Obj::HERO)
|
||||||
{
|
{
|
||||||
@@ -350,9 +343,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
|
|||||||
transferFrom2to1(secondHero, firstHero);
|
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);
|
answerQuery(query, 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -399,9 +389,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
|
|||||||
vstd::erase_if_present(visitableObjs, obj);
|
vstd::erase_if_present(visitableObjs, obj);
|
||||||
vstd::erase_if_present(alreadyVisited, 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
|
std::function<bool(const Goals::TSubgoal &)> checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool
|
||||||
{
|
{
|
||||||
if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum()))
|
if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum()))
|
||||||
@@ -412,27 +399,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
|
|||||||
return false;
|
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
|
//TODO: Find better way to handle hero boat removal
|
||||||
if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
|
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(visitableObjs, hero->boat);
|
||||||
vstd::erase_if_present(alreadyVisited, 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);
|
LOG_TRACE_PARAMS(logAi, "what '%i'", what);
|
||||||
NET_EVENT_HANDLER;
|
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)
|
void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain)
|
||||||
@@ -855,22 +815,7 @@ void VCAI::makeTurn()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(nullkiller)
|
nullkiller->makeTurn();
|
||||||
{
|
|
||||||
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 debug purpose
|
||||||
for (auto h : cb->getHeroesInfo())
|
for (auto h : cb->getHeroesInfo())
|
||||||
@@ -904,194 +849,6 @@ std::vector<HeroPtr> VCAI::getMyHeroes() const
|
|||||||
return ret;
|
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)
|
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());
|
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:
|
case Obj::CREATURE_GENERATOR1:
|
||||||
recruitCreatures(dynamic_cast<const CGDwelling *>(obj), h.get());
|
recruitCreatures(dynamic_cast<const CGDwelling *>(obj), h.get());
|
||||||
checkHeroArmy(h);
|
|
||||||
break;
|
break;
|
||||||
case Obj::TOWN:
|
case Obj::TOWN:
|
||||||
if(h->visitedTown) //we are inside, not just attacking
|
if(h->visitedTown) //we are inside, not just attacking
|
||||||
@@ -1116,7 +872,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
|||||||
ah->update();
|
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))
|
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||||
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
|
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
|
||||||
@@ -1127,7 +883,6 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
|||||||
makePossibleUpgrades(h.get());
|
makePossibleUpgrades(h.get());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
completeGoal(sptr(Goals::VisitObj(obj->id.getNum()).sethero(h)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::moveCreaturesToHero(const CGTownInstance * t)
|
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
|
//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)
|
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;
|
int count = d->creatures[i].first;
|
||||||
CreatureID creID = d->creatures[i].second.back();
|
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)
|
if(count > 0)
|
||||||
cb->recruitCreatures(d, recruiter, creID, count, i);
|
cb->recruitCreatures(d, recruiter, creID, count, i);
|
||||||
}
|
}
|
||||||
@@ -1402,250 +1153,6 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
|
|||||||
return true;
|
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)
|
void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
|
||||||
{
|
{
|
||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
@@ -1686,19 +1193,6 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
|
|||||||
alreadyVisited.insert(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)
|
void VCAI::markHeroUnableToExplore(HeroPtr h)
|
||||||
{
|
{
|
||||||
heroesUnableToExplore.insert(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
|
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());
|
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
|
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
|
vstd::erase_if_present(lockedHeroes, h); //hero seemingly is confused or has only 95mp which is not enough to move
|
||||||
invalidPathHeroes.insert(h);
|
invalidPathHeroes.insert(h);
|
||||||
throw cannotFulfillGoalException("Invalid path found!");
|
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);
|
logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos.toString(), h->visitablePos().toString(), ret);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2181,7 +1665,6 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
|
|||||||
if(g.hero->diggingStatus() == EDiggingStatus::CAN_DIG)
|
if(g.hero->diggingStatus() == EDiggingStatus::CAN_DIG)
|
||||||
{
|
{
|
||||||
cb->dig(g.hero.get());
|
cb->dig(g.hero.get());
|
||||||
completeGoal(sptr(g)); // finished digging
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2192,7 +1675,7 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
|
|||||||
|
|
||||||
void VCAI::tryRealize(Goals::Trade & g) //trade
|
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));
|
throw goalFulfilledException(sptr(g));
|
||||||
|
|
||||||
int accquiredResources = 0;
|
int accquiredResources = 0;
|
||||||
@@ -2200,7 +1683,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
|
|||||||
{
|
{
|
||||||
if(const IMarket * m = IMarket::castFrom(obj, false))
|
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++)
|
for(auto it = Res::ResourceSet::nziterator(freeRes); it.valid(); it++)
|
||||||
{
|
{
|
||||||
auto res = it->resType;
|
auto res = it->resType;
|
||||||
@@ -2217,7 +1700,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
|
|||||||
accquiredResources = toGet * (it->resVal / toGive);
|
accquiredResources = toGet * (it->resVal / toGive);
|
||||||
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
|
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
|
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++)
|
for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
|
||||||
{
|
{
|
||||||
auto res = ah->allResources();
|
auto res = cb->getResourceAmount();
|
||||||
auto & ci = armyToBuy[i];
|
auto & ci = armyToBuy[i];
|
||||||
|
|
||||||
if(g.objid != -1 && ci.creID != g.objid)
|
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());
|
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)
|
void VCAI::buildArmyIn(const CGTownInstance * t)
|
||||||
{
|
{
|
||||||
makePossibleUpgrades(t->visitingHero);
|
makePossibleUpgrades(t->visitingHero);
|
||||||
@@ -2507,16 +1856,6 @@ void VCAI::buildArmyIn(const CGTownInstance * t)
|
|||||||
moveCreaturesToHero(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)
|
void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
|
||||||
{
|
{
|
||||||
logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
|
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()
|
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)
|
if(makingTurn)
|
||||||
{
|
{
|
||||||
makingTurn->interrupt();
|
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);
|
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);
|
vstd::erase_if_present(visitedHeroes, h);
|
||||||
for (auto heroVec : visitedHeroes)
|
for (auto heroVec : visitedHeroes)
|
||||||
{
|
{
|
||||||
vstd::erase_if_present(heroVec.second, h);
|
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)
|
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_MAGIC:
|
||||||
case Obj::SCHOOL_OF_WAR:
|
case Obj::SCHOOL_OF_WAR:
|
||||||
{
|
{
|
||||||
if (ai->ah->freeGold() < 1000)
|
if (cb->getResourceAmount(Res::GOLD) < 1000)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2924,7 +2235,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
|||||||
break;
|
break;
|
||||||
case Obj::TREE_OF_KNOWLEDGE:
|
case Obj::TREE_OF_KNOWLEDGE:
|
||||||
{
|
{
|
||||||
TResources myRes = ai->ah->freeResources();
|
TResources myRes = cb->getResourceAmount();
|
||||||
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
|
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
@@ -2939,7 +2250,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
|||||||
//TODO: only on request
|
//TODO: only on request
|
||||||
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
|
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
|
||||||
return false;
|
return false;
|
||||||
else if(ai->ah->freeGold() < GameConstants::HERO_GOLD_COST)
|
else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -13,9 +13,7 @@
|
|||||||
#include "Goals/AbstractGoal.h"
|
#include "Goals/AbstractGoal.h"
|
||||||
#include "../../lib/AI_Base.h"
|
#include "../../lib/AI_Base.h"
|
||||||
#include "../../CCallback.h"
|
#include "../../CCallback.h"
|
||||||
|
|
||||||
#include "../../lib/CThreadHelper.h"
|
#include "../../lib/CThreadHelper.h"
|
||||||
|
|
||||||
#include "../../lib/GameConstants.h"
|
#include "../../lib/GameConstants.h"
|
||||||
#include "../../lib/VCMI_Lib.h"
|
#include "../../lib/VCMI_Lib.h"
|
||||||
#include "../../lib/CBuildingHandler.h"
|
#include "../../lib/CBuildingHandler.h"
|
||||||
@@ -90,12 +88,6 @@ public:
|
|||||||
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
|
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
|
||||||
std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
|
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::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
|
||||||
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
|
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
|
||||||
std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
|
std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
|
||||||
@@ -113,6 +105,9 @@ public:
|
|||||||
std::shared_ptr<CCallback> myCb;
|
std::shared_ptr<CCallback> myCb;
|
||||||
|
|
||||||
std::unique_ptr<boost::thread> makingTurn;
|
std::unique_ptr<boost::thread> makingTurn;
|
||||||
|
private:
|
||||||
|
boost::mutex turnInterruptionMutex;
|
||||||
|
public:
|
||||||
ObjectInstanceID selectedObject;
|
ObjectInstanceID selectedObject;
|
||||||
|
|
||||||
AIhelper * ah;
|
AIhelper * ah;
|
||||||
@@ -199,17 +194,9 @@ public:
|
|||||||
void battleEnd(const BattleResult * br) override;
|
void battleEnd(const BattleResult * br) override;
|
||||||
|
|
||||||
void makeTurn();
|
void makeTurn();
|
||||||
void mainLoop();
|
|
||||||
void performTypicalActions();
|
|
||||||
|
|
||||||
void buildArmyIn(const CGTownInstance * t);
|
void buildArmyIn(const CGTownInstance * t);
|
||||||
void striveToGoal(Goals::TSubgoal ultimateGoal);
|
|
||||||
Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal);
|
|
||||||
void endTurn();
|
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);
|
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
||||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
|
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
|
||||||
@@ -230,8 +217,6 @@ public:
|
|||||||
|
|
||||||
void addVisitableObj(const CGObjectInstance * obj);
|
void addVisitableObj(const CGObjectInstance * obj);
|
||||||
void markObjectVisited(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 markHeroUnableToExplore(HeroPtr h);
|
||||||
void markHeroAbleToExplore(HeroPtr h);
|
void markHeroAbleToExplore(HeroPtr h);
|
||||||
@@ -261,7 +246,6 @@ public:
|
|||||||
std::vector<HeroPtr> getUnblockedHeroes() const;
|
std::vector<HeroPtr> getUnblockedHeroes() const;
|
||||||
std::vector<HeroPtr> getMyHeroes() const;
|
std::vector<HeroPtr> getMyHeroes() const;
|
||||||
HeroPtr primaryHero() const;
|
HeroPtr primaryHero() const;
|
||||||
void checkHeroArmy(HeroPtr h);
|
|
||||||
|
|
||||||
void requestSent(const CPackForServer * pack, int requestID) override;
|
void requestSent(const CPackForServer * pack, int requestID) override;
|
||||||
void answerQuery(QueryID queryID, int selection);
|
void answerQuery(QueryID queryID, int selection);
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>{276C3DB0-7A6B-4417-8E5C-322B08633AAC}</ProjectGuid>
|
<ProjectGuid>{276C3DB0-7A6B-4417-8E5C-322B08633AAC}</ProjectGuid>
|
||||||
<RootNamespace>StupidAI</RootNamespace>
|
<RootNamespace>StupidAI</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
Reference in New Issue
Block a user