mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
I have no idea what I'm doing
This commit is contained in:
parent
75f8c8b29a
commit
273802c92c
@ -126,6 +126,11 @@ const CGHeroInstance * HeroPtr::operator*() const
|
||||
return get();
|
||||
}
|
||||
|
||||
bool HeroPtr::operator==(const HeroPtr & rhs) const
|
||||
{
|
||||
return h == rhs.get(true);
|
||||
}
|
||||
|
||||
void foreach_tile_pos(std::function<void(const int3 & pos)> foo)
|
||||
{
|
||||
// some micro-optimizations since this function gets called a LOT
|
||||
@ -475,6 +480,50 @@ void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & o
|
||||
}
|
||||
}
|
||||
|
||||
creInfo infoFromDC(const dwellingContent & dc)
|
||||
{
|
||||
creInfo ci;
|
||||
ci.count = dc.first;
|
||||
ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
|
||||
if (ci.creID != -1)
|
||||
{
|
||||
ci.cre = VLC->creh->creatures[ci.creID];
|
||||
ci.level = ci.cre->level; //this is cretaure tier, while tryRealize expects dwelling level. Ignore.
|
||||
}
|
||||
else
|
||||
{
|
||||
ci.cre = nullptr;
|
||||
ci.level = 0;
|
||||
}
|
||||
return ci;
|
||||
}
|
||||
|
||||
ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGTownInstance * t)
|
||||
{
|
||||
ui64 aivalue = 0;
|
||||
|
||||
int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
|
||||
for (auto const dc : t->creatures)
|
||||
{
|
||||
creInfo ci = infoFromDC(dc);
|
||||
if (ci.count && ci.creID != -1) //valid creature at this level
|
||||
{
|
||||
//can be merged with another stack?
|
||||
SlotID dst = h->getSlotFor(ci.creID);
|
||||
if (!h->hasStackAtSlot(dst)) //need another new slot for this stack
|
||||
if (!freeHeroSlots) //no more place for stacks
|
||||
continue;
|
||||
else
|
||||
freeHeroSlots--; //new slot will be occupied
|
||||
|
||||
//we found matching occupied or free slot
|
||||
aivalue += ci.count * ci.cre->AIValue;
|
||||
}
|
||||
}
|
||||
|
||||
return aivalue;
|
||||
}
|
||||
|
||||
ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t)
|
||||
{
|
||||
ui64 ret = 0;
|
||||
|
@ -20,9 +20,11 @@
|
||||
#include "../../lib/CPathfinder.h"
|
||||
|
||||
class CCallback;
|
||||
struct creInfo;
|
||||
|
||||
typedef const int3 & crint3;
|
||||
typedef const std::string & crstring;
|
||||
typedef std::pair<ui32, std::vector<CreatureID>> dwellingContent;
|
||||
|
||||
const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1;
|
||||
const int ACTUAL_RESOURCE_COUNT = 7;
|
||||
@ -35,7 +37,7 @@ extern const int GOLD_RESERVE;
|
||||
//provisional class for AI to store a reference to an owned hero object
|
||||
//checks if it's valid on access, should be used in place of const CGHeroInstance*
|
||||
|
||||
struct HeroPtr
|
||||
struct DLL_EXPORT HeroPtr
|
||||
{
|
||||
const CGHeroInstance * h;
|
||||
ObjectInstanceID hid;
|
||||
@ -56,6 +58,7 @@ public:
|
||||
bool operator<(const HeroPtr & rhs) const;
|
||||
const CGHeroInstance * operator->() const;
|
||||
const CGHeroInstance * operator*() const; //not that consistent with -> but all interfaces use CGHeroInstance*, so it's convenient
|
||||
bool operator==(const HeroPtr & rhs) const;
|
||||
|
||||
const CGHeroInstance * get(bool doWeExpectNull = false) const;
|
||||
bool validAndSet() const;
|
||||
@ -137,6 +140,15 @@ bool objWithID(const CGObjectInstance * obj)
|
||||
return obj->ID == id;
|
||||
}
|
||||
|
||||
struct creInfo
|
||||
{
|
||||
int count;
|
||||
CreatureID creID;
|
||||
CCreature * cre;
|
||||
int level;
|
||||
};
|
||||
creInfo infoFromDC(const dwellingContent & dc);
|
||||
|
||||
void foreach_tile_pos(std::function<void(const int3 & pos)> foo);
|
||||
void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 & pos)> foo); // avoid costly retrieval of thread-specific pointer
|
||||
void foreach_neighbour(const int3 & pos, std::function<void(const int3 & pos)> foo);
|
||||
@ -160,6 +172,7 @@ bool compareMovement(HeroPtr lhs, HeroPtr rhs);
|
||||
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
|
||||
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
||||
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
||||
ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGTownInstance * t);
|
||||
ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t);
|
||||
int3 whereToExplore(HeroPtr h);
|
||||
|
||||
@ -173,4 +186,4 @@ public:
|
||||
{
|
||||
}
|
||||
bool operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs);
|
||||
};
|
||||
};
|
84
AI/VCAI/AIhelper.cpp
Normal file
84
AI/VCAI/AIhelper.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* AIhelper.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
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "AIhelper.h"
|
||||
#include "ResourceManager.h"
|
||||
|
||||
boost::thread_specific_ptr<AIhelper> ah;
|
||||
|
||||
AIhelper::AIhelper()
|
||||
{
|
||||
resourceManager.reset(new ResourceManager);
|
||||
}
|
||||
|
||||
AIhelper::~AIhelper()
|
||||
{
|
||||
}
|
||||
|
||||
bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal)
|
||||
{
|
||||
return resourceManager->notifyGoalCompleted(goal);
|
||||
}
|
||||
|
||||
void AIhelper::setCB(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
resourceManager->setCB(CB);
|
||||
}
|
||||
|
||||
void AIhelper::setAI(VCAI * AI)
|
||||
{
|
||||
resourceManager->setAI(AI);
|
||||
}
|
||||
|
||||
Goals::TSubgoal AIhelper::whatToDo(TResources & res, Goals::TSubgoal goal)
|
||||
{
|
||||
return resourceManager->whatToDo(res, goal);
|
||||
}
|
||||
|
||||
Goals::TSubgoal AIhelper::whatToDo() const
|
||||
{
|
||||
return resourceManager->whatToDo();
|
||||
}
|
||||
|
||||
bool AIhelper::hasTasksLeft() const
|
||||
{
|
||||
return resourceManager->hasTasksLeft();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
52
AI/VCAI/AIhelper.h
Normal file
52
AI/VCAI/AIhelper.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* AIhelper.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
|
||||
|
||||
/*
|
||||
!!! Note: Include THIS file at the end of include list to avoid "undefined base class" error
|
||||
*/
|
||||
|
||||
#include "ResourceManager.h"
|
||||
|
||||
class ResourceManager;
|
||||
|
||||
//indirection interface for various modules
|
||||
class DLL_EXPORT AIhelper : public IResourceManager
|
||||
{
|
||||
friend class VCAI;
|
||||
friend struct SetGlobalState; //mess?
|
||||
|
||||
//members are thread_specific. AIhelper is global
|
||||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<VCAI> ai;
|
||||
public:
|
||||
AIhelper();
|
||||
~AIhelper();
|
||||
|
||||
//TODO: consider common interface with Resource Manager?
|
||||
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; //peek highest-priority goal
|
||||
bool hasTasksLeft() const override;
|
||||
|
||||
private:
|
||||
bool notifyGoalCompleted(Goals::TSubgoal goal);
|
||||
|
||||
void setCB(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
};
|
||||
|
@ -322,7 +322,7 @@ float FuzzyHelper::evaluate(Goals::Explore & g)
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::RecruitHero & g)
|
||||
{
|
||||
return 1; //just try to recruit hero as one of options
|
||||
return 1;
|
||||
}
|
||||
FuzzyHelper::EvalVisitTile::~EvalVisitTile()
|
||||
{
|
||||
@ -518,7 +518,7 @@ float FuzzyHelper::evaluate(Goals::ClearWayTo & g)
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::BuildThis & g)
|
||||
{
|
||||
return 1;
|
||||
return g.priority; //TODO
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::DigAtTile & g)
|
||||
{
|
||||
@ -526,12 +526,16 @@ float FuzzyHelper::evaluate(Goals::DigAtTile & g)
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::CollectRes & g)
|
||||
{
|
||||
return 0;
|
||||
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::Invalid & g)
|
||||
{
|
||||
return -1e10;
|
||||
@ -541,7 +545,7 @@ float FuzzyHelper::evaluate(Goals::AbstractGoal & g)
|
||||
logAi->warn("Cannot evaluate goal %s", g.name());
|
||||
return g.priority;
|
||||
}
|
||||
void FuzzyHelper::setPriority(Goals::TSubgoal & g)
|
||||
void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pattern
|
||||
{
|
||||
g->setpriority(g->accept(this)); //this enforces returned value is set
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public:
|
||||
float evaluate(Goals::DigAtTile & g);
|
||||
float evaluate(Goals::CollectRes & g);
|
||||
float evaluate(Goals::Build & g);
|
||||
float evaluate(Goals::BuyArmy & g);
|
||||
float evaluate(Goals::GatherArmy & g);
|
||||
float evaluate(Goals::ClearWayTo & g);
|
||||
float evaluate(Goals::Invalid & g);
|
||||
|
@ -11,18 +11,23 @@
|
||||
#include "Goals.h"
|
||||
#include "VCAI.h"
|
||||
#include "Fuzzy.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../lib/CPathfinder.h"
|
||||
#include "StringConstants.h"
|
||||
|
||||
#include "AIhelper.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh; //TODO: this logic should be moved inside VCAI
|
||||
extern boost::thread_specific_ptr<AIhelper> ah;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
TSubgoal Goals::sptr(const AbstractGoal & tmp)
|
||||
{
|
||||
std::shared_ptr<AbstractGoal> ptr;
|
||||
TSubgoal ptr;
|
||||
ptr.reset(tmp.clone());
|
||||
return ptr;
|
||||
}
|
||||
@ -48,6 +53,9 @@ std::string Goals::AbstractGoal::name() const //TODO: virtualize
|
||||
case GATHER_ARMY:
|
||||
desc = "GATHER ARMY";
|
||||
break;
|
||||
case BUY_ARMY:
|
||||
return "BUY ARMY";
|
||||
break;
|
||||
case BOOST_HERO:
|
||||
desc = "BOOST_HERO (unsupported)";
|
||||
break;
|
||||
@ -56,7 +64,7 @@ std::string Goals::AbstractGoal::name() const //TODO: virtualize
|
||||
case BUILD_STRUCTURE:
|
||||
return "BUILD STRUCTURE";
|
||||
case COLLECT_RES:
|
||||
desc = "COLLECT RESOURCE";
|
||||
desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + boost::lexical_cast<std::string>(value) + ")";
|
||||
break;
|
||||
case GATHER_TROOPS:
|
||||
desc = "GATHER TROOPS";
|
||||
@ -114,21 +122,25 @@ bool Goals::AbstractGoal::operator==(AbstractGoal & g)
|
||||
case INVALID:
|
||||
case WIN:
|
||||
case DO_NOT_LOSE:
|
||||
case RECRUIT_HERO: //recruit any hero, as yet
|
||||
case RECRUIT_HERO: //overloaded
|
||||
return true;
|
||||
break;
|
||||
|
||||
//assigned to hero, no parameters
|
||||
case CONQUER:
|
||||
case EXPLORE:
|
||||
case GATHER_ARMY: //actual value is indifferent
|
||||
case BOOST_HERO:
|
||||
return g.hero.h == hero.h; //how comes HeroPtrs are equal for different heroes?
|
||||
break;
|
||||
|
||||
case GATHER_ARMY: //actual value is indifferent
|
||||
return (g.hero.h == hero.h || town == g.town); //TODO: gather army for town maybe?
|
||||
break;
|
||||
|
||||
//assigned hero and tile
|
||||
case VISIT_TILE:
|
||||
case CLEAR_WAY_TO:
|
||||
case DIG_AT_TILE:
|
||||
return (g.hero.h == hero.h && g.tile == tile);
|
||||
break;
|
||||
|
||||
@ -137,16 +149,20 @@ bool Goals::AbstractGoal::operator==(AbstractGoal & g)
|
||||
case FIND_OBJ: //TODO: use subtype?
|
||||
case VISIT_HERO:
|
||||
case GET_ART_TYPE:
|
||||
case DIG_AT_TILE:
|
||||
return (g.hero.h == hero.h && g.objid == objid);
|
||||
break;
|
||||
|
||||
case BUILD_STRUCTURE:
|
||||
return (town == g.town && bid == g.bid); //build specific structure in specific town
|
||||
break;
|
||||
|
||||
//no check atm
|
||||
case COLLECT_RES:
|
||||
return (resID == g.resID); //every hero may collect resources
|
||||
break;
|
||||
case GATHER_TROOPS:
|
||||
case ISSUE_COMMAND:
|
||||
case BUILD: //TODO: should be decomposed to build specific structures
|
||||
case BUILD_STRUCTURE:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -156,28 +172,56 @@ bool Goals::AbstractGoal::operator==(AbstractGoal & g)
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
template<>
|
||||
void CGoal<Win>::accept(VCAI * ai)
|
||||
{
|
||||
ai->tryRealize(static_cast<Win &>(*this));
|
||||
}
|
||||
template<>
|
||||
void CGoal<Win>::accept(VCAI * ai)
|
||||
{
|
||||
ai->tryRealize(static_cast<Win &>(*this));
|
||||
}
|
||||
|
||||
template<>
|
||||
void CGoal<Build>::accept(VCAI * ai)
|
||||
{
|
||||
ai->tryRealize(static_cast<Build &>(*this));
|
||||
}
|
||||
template<>
|
||||
float CGoal<Win>::accept(FuzzyHelper * f)
|
||||
{
|
||||
return f->evaluate(static_cast<Win &>(*this));
|
||||
}
|
||||
template<>
|
||||
void CGoal<Build>::accept(VCAI * ai)
|
||||
{
|
||||
ai->tryRealize(static_cast<Build &>(*this));
|
||||
}
|
||||
template<>
|
||||
float CGoal<Win>::accept(FuzzyHelper * f)
|
||||
{
|
||||
return f->evaluate(static_cast<Win &>(*this));
|
||||
}
|
||||
|
||||
template<>
|
||||
float CGoal<Build>::accept(FuzzyHelper * f)
|
||||
{
|
||||
return f->evaluate(static_cast<Build &>(*this));
|
||||
}
|
||||
template<>
|
||||
float CGoal<Build>::accept(FuzzyHelper * f)
|
||||
{
|
||||
return f->evaluate(static_cast<Build &>(*this));
|
||||
}
|
||||
bool TSubgoal::operator==(const TSubgoal & rhs) const
|
||||
{
|
||||
return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match
|
||||
}
|
||||
|
||||
bool BuyArmy::operator==(BuyArmy & g)
|
||||
{
|
||||
//if (hero && hero != g.hero)
|
||||
// return false;
|
||||
return town == g.town;
|
||||
}
|
||||
bool BuyArmy::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
//if (hero && hero != goal->hero)
|
||||
// return false;
|
||||
return town == goal->town && goal->value >= value; //can always buy more army
|
||||
}
|
||||
TSubgoal BuyArmy::whatToDoToAchieve()
|
||||
{
|
||||
//TODO: calculate the actual cost of units instead
|
||||
TResources price;
|
||||
price[Res::GOLD] = value * 0.4f; //some approximate value
|
||||
return 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") % boost::lexical_cast<std::string>(value), town->name;
|
||||
}
|
||||
}
|
||||
|
||||
//TSubgoal AbstractGoal::whatToDoToAchieve()
|
||||
@ -251,7 +295,7 @@ TSubgoal Win::whatToDoToAchieve()
|
||||
if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
|
||||
{
|
||||
const CGTownInstance * t = h->visitedTown;
|
||||
return sptr(Goals::BuildThis(BuildingID::GRAIL, t));
|
||||
return sptr(Goals::BuildThis(BuildingID::GRAIL, t).setpriority(10));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -371,6 +415,19 @@ TSubgoal FindObj::whatToDoToAchieve()
|
||||
return sptr(Goals::Explore());
|
||||
}
|
||||
|
||||
bool Goals::FindObj::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == Goals::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;
|
||||
}
|
||||
|
||||
std::string GetObj::completeMessage() const
|
||||
{
|
||||
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
|
||||
@ -403,13 +460,15 @@ TSubgoal GetObj::whatToDoToAchieve()
|
||||
|
||||
bool GetObj::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if(goal->goalType == Goals::VISIT_TILE)
|
||||
if(goal->goalType == Goals::VISIT_TILE) //visiting tile visits object at same time
|
||||
{
|
||||
auto obj = cb->getObj(ObjectInstanceID(objid));
|
||||
if(obj && obj->visitablePos() == goal->tile) //object could be removed
|
||||
return true;
|
||||
if (!hero || hero == goal->hero)
|
||||
{
|
||||
auto obj = cb->getObj(ObjectInstanceID(objid));
|
||||
if (obj && obj->visitablePos() == goal->tile) //object could be removed
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -441,17 +500,18 @@ TSubgoal VisitHero::whatToDoToAchieve()
|
||||
|
||||
bool VisitHero::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if(goal->goalType != Goals::VISIT_TILE)
|
||||
//TODO: VisitObj shoudl not be used for heroes, but...
|
||||
if(goal->goalType == Goals::VISIT_TILE)
|
||||
{
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
TSubgoal GetArtOfType::whatToDoToAchieve()
|
||||
@ -474,6 +534,14 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
|
||||
return (fh->chooseSolution(getAllPossibleSubgoals()));
|
||||
}
|
||||
|
||||
bool Goals::ClearWayTo::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == Goals::VISIT_TILE)
|
||||
if (!hero || hero == goal->hero)
|
||||
return tile == goal->tile;
|
||||
return false;
|
||||
}
|
||||
|
||||
TGoalVec ClearWayTo::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec ret;
|
||||
@ -702,17 +770,21 @@ bool Explore::fulfillsMe(TSubgoal goal)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TSubgoal RecruitHero::whatToDoToAchieve()
|
||||
{
|
||||
const CGTownInstance * t = ai->findTownWithTavern();
|
||||
if(!t)
|
||||
return sptr(Goals::BuildThis(BuildingID::TAVERN));
|
||||
return sptr(Goals::BuildThis(BuildingID::TAVERN).setpriority(2));
|
||||
|
||||
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
|
||||
return sptr(Goals::CollectRes(Res::GOLD, GameConstants::HERO_GOLD_COST));
|
||||
TResources res;
|
||||
res[Res::GOLD] = GameConstants::HERO_GOLD_COST;
|
||||
return ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
|
||||
}
|
||||
|
||||
return iAmElementar();
|
||||
bool Goals::RecruitHero::operator==(RecruitHero & g)
|
||||
{
|
||||
//TODO: check town and hero
|
||||
return true; //for now, recruiting any hero will do
|
||||
}
|
||||
|
||||
std::string VisitTile::completeMessage() const
|
||||
@ -798,25 +870,158 @@ TSubgoal DigAtTile::whatToDoToAchieve()
|
||||
|
||||
TSubgoal BuildThis::whatToDoToAchieve()
|
||||
{
|
||||
//TODO check res
|
||||
//look for town
|
||||
//prerequisites?
|
||||
return iAmElementar();
|
||||
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
|
||||
{
|
||||
auto res = town->town->buildings.at(BuildingID(bid))->resources;
|
||||
return ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
|
||||
}
|
||||
else
|
||||
throw cannotFulfillGoalException("Cannot find town to build this");
|
||||
}
|
||||
|
||||
TGoalVec Goals::CollectRes::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec ret;
|
||||
|
||||
auto givesResource = [this](const CGObjectInstance * obj) -> bool
|
||||
{
|
||||
//TODO: move this logic to object side
|
||||
//TODO: remember mithril exists
|
||||
//TODO: water objects
|
||||
//TODO: Creature banks
|
||||
|
||||
//return false first from once-visitable, before checking if they were even visited
|
||||
switch (obj->ID.num)
|
||||
{
|
||||
case Obj::TREASURE_CHEST:
|
||||
return resID == Res::GOLD;
|
||||
break;
|
||||
case Obj::RESOURCE:
|
||||
return obj->subID == resID;
|
||||
break;
|
||||
case Obj::MINE:
|
||||
return (obj->subID == resID &&
|
||||
(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
|
||||
break;
|
||||
case Obj::CAMPFIRE:
|
||||
return true; //contains all resources
|
||||
break;
|
||||
case Obj::WINDMILL:
|
||||
switch (resID)
|
||||
{
|
||||
case Res::GOLD:
|
||||
case Res::WOOD:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Obj::WATER_WHEEL:
|
||||
if (resID != Res::GOLD)
|
||||
return false;
|
||||
break;
|
||||
case Obj::MYSTICAL_GARDEN:
|
||||
if ((resID != Res::GOLD) && (resID != Res::GEMS))
|
||||
return false;
|
||||
break;
|
||||
case Obj::LEAN_TO:
|
||||
case Obj::WAGON:
|
||||
if (resID != Res::GOLD)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
|
||||
};
|
||||
|
||||
std::vector<const CGObjectInstance *> objs;
|
||||
for (auto obj : ai->visitableObjs)
|
||||
{
|
||||
if (givesResource(obj))
|
||||
objs.push_back(obj);
|
||||
}
|
||||
for (auto h : cb->getHeroesInfo())
|
||||
{
|
||||
auto sm = ai->getCachedSectorMap(h);
|
||||
std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
|
||||
|
||||
for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
|
||||
{
|
||||
if (givesResource(obj))
|
||||
ourObjs.push_back(obj);
|
||||
}
|
||||
for (auto obj : ourObjs)
|
||||
{
|
||||
int3 dest = obj->visitablePos();
|
||||
auto t = sm->firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded
|
||||
if (t.valid()) //we know any path at all
|
||||
{
|
||||
if (ai->isTileNotReserved(h, t)) //no other hero wants to conquer that tile
|
||||
{
|
||||
if (isSafeToVisit(h, dest))
|
||||
{
|
||||
if (dest != t) //there is something blocking our way
|
||||
ret.push_back(sptr(Goals::ClearWayTo(dest, h).setisAbstract(true)));
|
||||
else
|
||||
{
|
||||
ret.push_back(sptr(Goals::VisitTile(dest).sethero(h).setisAbstract(true)));
|
||||
}
|
||||
}
|
||||
else //we need to get army in order to pick that object
|
||||
ret.push_back(sptr(Goals::GatherArmy(evaluateDanger(dest, h) * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TSubgoal CollectRes::whatToDoToAchieve()
|
||||
{
|
||||
auto goals = getAllPossibleSubgoals();
|
||||
auto trade = whatToDoToTrade();
|
||||
if (!trade->invalid())
|
||||
goals.push_back(trade);
|
||||
|
||||
if (goals.empty())
|
||||
return sptr(Goals::Explore()); //we can always do that
|
||||
else
|
||||
return fh->chooseSolution(goals); //TODO: evaluate trading
|
||||
}
|
||||
|
||||
TSubgoal Goals::CollectRes::whatToDoToTrade()
|
||||
{
|
||||
std::vector<const IMarket *> markets;
|
||||
|
||||
std::vector<const CGObjectInstance *> visObjs;
|
||||
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);
|
||||
else if(obj->ID == Obj::TRADING_POST) //TODO a moze po prostu test na pozwalanie handlu?
|
||||
else if (obj->ID == Obj::TRADING_POST)
|
||||
markets.push_back(m);
|
||||
}
|
||||
}
|
||||
@ -828,20 +1033,20 @@ TSubgoal CollectRes::whatToDoToAchieve()
|
||||
|
||||
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 false;
|
||||
}), 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)
|
||||
return sptr(Goals::BuildThis(BuildingID::MARKETPLACE, t));
|
||||
if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
|
||||
return sptr(Goals::BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -849,9 +1054,9 @@ TSubgoal CollectRes::whatToDoToAchieve()
|
||||
const IMarket * m = markets.back();
|
||||
//attempt trade at back (best prices)
|
||||
int howManyCanWeBuy = 0;
|
||||
for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
||||
for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
||||
{
|
||||
if(i == resID)
|
||||
if (i == resID)
|
||||
continue;
|
||||
int toGive = -1, toReceive = -1;
|
||||
m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
|
||||
@ -859,21 +1064,36 @@ TSubgoal CollectRes::whatToDoToAchieve()
|
||||
howManyCanWeBuy += toReceive * (cb->getResourceAmount(i) / toGive);
|
||||
}
|
||||
|
||||
if(howManyCanWeBuy + cb->getResourceAmount(static_cast<Res::ERes>(resID)) >= value)
|
||||
if (howManyCanWeBuy >= value)
|
||||
{
|
||||
auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
||||
assert(backObj);
|
||||
if(backObj->tempOwner != ai->playerID)
|
||||
auto objid = m->o->id.getNum();
|
||||
if (backObj->tempOwner != ai->playerID)
|
||||
{
|
||||
return sptr(Goals::GetObj(m->o->id.getNum()));
|
||||
return sptr(Goals::GetObj(objid));
|
||||
}
|
||||
else
|
||||
{
|
||||
return sptr(Goals::GetObj(m->o->id.getNum()).setisElementar(true));
|
||||
if (m->o->ID == Obj::TOWN) //just trade remotely using town objid
|
||||
return sptr(setobjid(objid).setisElementar(true));
|
||||
else //just go there
|
||||
return sptr(Goals::GetObj(objid).setisElementar(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sptr(setisElementar(true)); //all the conditions for trade are met
|
||||
return sptr(Goals::Invalid()); //cannot trade
|
||||
//TODO: separate goal to execute trade?
|
||||
//return sptr(setisElementar(true)); //not sure why we are here
|
||||
}
|
||||
|
||||
bool CollectRes::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->resID == resID)
|
||||
if (goal->value >= value)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
@ -899,7 +1119,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
}
|
||||
else
|
||||
{
|
||||
return sptr(Goals::BuildThis(bid, t));
|
||||
return sptr(Goals::BuildThis(bid, t).setpriority(priority));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -915,7 +1135,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
{
|
||||
for(auto type : creature.second)
|
||||
{
|
||||
if(type == objid && ai->freeResources().canAfford(VLC->creh->creatures[type]->cost))
|
||||
if(type == objid && ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
|
||||
dwellings.push_back(d);
|
||||
}
|
||||
}
|
||||
@ -962,6 +1182,15 @@ TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
//TODO: exchange troops between heroes
|
||||
}
|
||||
|
||||
bool Goals::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;
|
||||
}
|
||||
|
||||
TSubgoal Conquer::whatToDoToAchieve()
|
||||
{
|
||||
return fh->chooseSolution(getAllPossibleSubgoals());
|
||||
@ -1050,6 +1279,14 @@ TSubgoal Build::whatToDoToAchieve()
|
||||
return iAmElementar();
|
||||
}
|
||||
|
||||
bool Goals::Build::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == Goals::BUILD || goal->goalType == Goals::BUILD_STRUCTURE)
|
||||
return (!town || town == goal->town); //building anything will do, in this town if set
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
TSubgoal Invalid::whatToDoToAchieve()
|
||||
{
|
||||
return iAmElementar();
|
||||
@ -1082,14 +1319,25 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
|
||||
auto pos = t->visitablePos();
|
||||
if(ai->isAccessibleForHero(pos, hero))
|
||||
{
|
||||
//grab army from town
|
||||
if(!t->visitingHero && howManyReinforcementsCanGet(hero, t))
|
||||
{
|
||||
if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
|
||||
ret.push_back(sptr(Goals::VisitTile(pos).sethero(hero)));
|
||||
}
|
||||
//buy army in town
|
||||
if (!t->visitingHero || t->visitingHero != hero.get(true))
|
||||
{
|
||||
ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, t));
|
||||
if (val)
|
||||
ret.push_back(sptr(Goals::BuyArmy(t, val).sethero(hero)));
|
||||
}
|
||||
//build dwelling
|
||||
auto bid = ai->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
|
||||
if(bid != BuildingID::NONE)
|
||||
ret.push_back(sptr(BuildThis(bid, t)));
|
||||
if (bid != BuildingID::NONE)
|
||||
{
|
||||
ret.push_back(sptr(BuildThis(bid, t).setpriority(priority)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1134,7 +1382,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
|
||||
for(auto & creatureID : creLevel.second)
|
||||
{
|
||||
auto creature = VLC->creh->creatures[creatureID];
|
||||
if(ai->freeResources().canAfford(creature->cost))
|
||||
if(ah->freeResources().canAfford(creature->cost))
|
||||
objs.push_back(obj);
|
||||
}
|
||||
}
|
||||
@ -1154,7 +1402,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
|
||||
}
|
||||
}
|
||||
|
||||
if(ai->canRecruitAnyHero() && ai->freeResources()[Res::GOLD] > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
|
||||
if(ai->canRecruitAnyHero() && ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
|
||||
{
|
||||
if(auto t = ai->findTownWithTavern())
|
||||
{
|
||||
|
111
AI/VCAI/Goals.h
111
AI/VCAI/Goals.h
@ -23,14 +23,21 @@ namespace Goals
|
||||
{
|
||||
class AbstractGoal;
|
||||
class VisitTile;
|
||||
typedef std::shared_ptr<Goals::AbstractGoal> TSubgoal;
|
||||
|
||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<Goals::AbstractGoal>
|
||||
{
|
||||
public:
|
||||
bool operator==(const TSubgoal & rhs) const;
|
||||
//TODO: serialize?
|
||||
};
|
||||
typedef std::vector<TSubgoal> TGoalVec;
|
||||
|
||||
enum EGoals
|
||||
{
|
||||
INVALID = -1,
|
||||
WIN, DO_NOT_LOSE, CONQUER, BUILD, //build needs to get a real reasoning
|
||||
EXPLORE, GATHER_ARMY, BOOST_HERO,
|
||||
EXPLORE, GATHER_ARMY,
|
||||
BOOST_HERO,
|
||||
RECRUIT_HERO,
|
||||
BUILD_STRUCTURE, //if hero set, then in visited town
|
||||
COLLECT_RES,
|
||||
@ -48,7 +55,8 @@ enum EGoals
|
||||
|
||||
VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
|
||||
CLEAR_WAY_TO,
|
||||
DIG_AT_TILE //elementar with hero on tile
|
||||
DIG_AT_TILE,//elementar with hero on tile
|
||||
BUY_ARMY //at specific town
|
||||
};
|
||||
|
||||
//method chaining + clone pattern
|
||||
@ -61,9 +69,9 @@ enum EGoals
|
||||
|
||||
enum {LOW_PR = -1};
|
||||
|
||||
TSubgoal sptr(const AbstractGoal & tmp);
|
||||
DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
|
||||
|
||||
class AbstractGoal
|
||||
class DLL_EXPORT AbstractGoal
|
||||
{
|
||||
public:
|
||||
bool isElementar; VSETTER(bool, isElementar)
|
||||
@ -129,7 +137,7 @@ public:
|
||||
virtual bool operator==(AbstractGoal & g);
|
||||
virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
|
||||
{
|
||||
return false;
|
||||
return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
|
||||
}
|
||||
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
@ -149,7 +157,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class CGoal : public AbstractGoal
|
||||
template<typename T> class DLL_EXPORT CGoal : public AbstractGoal
|
||||
{
|
||||
public:
|
||||
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
|
||||
@ -186,8 +194,8 @@ public:
|
||||
}
|
||||
TSubgoal iAmElementar()
|
||||
{
|
||||
setisElementar(true);
|
||||
std::shared_ptr<AbstractGoal> ptr;
|
||||
setisElementar(true); //FIXME: it's not const-correct, maybe we shoudl only set returned clone?
|
||||
TSubgoal ptr;
|
||||
ptr.reset(clone());
|
||||
return ptr;
|
||||
}
|
||||
@ -199,7 +207,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class Invalid : public CGoal<Invalid>
|
||||
class DLL_EXPORT Invalid : public CGoal<Invalid>
|
||||
{
|
||||
public:
|
||||
Invalid()
|
||||
@ -214,7 +222,7 @@ public:
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
};
|
||||
|
||||
class Win : public CGoal<Win>
|
||||
class DLL_EXPORT Win : public CGoal<Win>
|
||||
{
|
||||
public:
|
||||
Win()
|
||||
@ -229,7 +237,7 @@ public:
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
};
|
||||
|
||||
class NotLose : public CGoal<NotLose>
|
||||
class DLL_EXPORT NotLose : public CGoal<NotLose>
|
||||
{
|
||||
public:
|
||||
NotLose()
|
||||
@ -244,7 +252,7 @@ public:
|
||||
//TSubgoal whatToDoToAchieve() override;
|
||||
};
|
||||
|
||||
class Conquer : public CGoal<Conquer>
|
||||
class DLL_EXPORT Conquer : public CGoal<Conquer>
|
||||
{
|
||||
public:
|
||||
Conquer()
|
||||
@ -256,7 +264,7 @@ public:
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
};
|
||||
|
||||
class Build : public CGoal<Build>
|
||||
class DLL_EXPORT Build : public CGoal<Build>
|
||||
{
|
||||
public:
|
||||
Build()
|
||||
@ -269,9 +277,10 @@ public:
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
};
|
||||
|
||||
class Explore : public CGoal<Explore>
|
||||
class DLL_EXPORT Explore : public CGoal<Explore>
|
||||
{
|
||||
public:
|
||||
Explore()
|
||||
@ -291,7 +300,7 @@ public:
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
};
|
||||
|
||||
class GatherArmy : public CGoal<GatherArmy>
|
||||
class DLL_EXPORT GatherArmy : public CGoal<GatherArmy>
|
||||
{
|
||||
public:
|
||||
GatherArmy()
|
||||
@ -309,7 +318,28 @@ public:
|
||||
std::string completeMessage() const override;
|
||||
};
|
||||
|
||||
class BoostHero : public CGoal<BoostHero>
|
||||
class DLL_EXPORT BuyArmy : public CGoal<BuyArmy>
|
||||
{
|
||||
private:
|
||||
BuyArmy()
|
||||
: CGoal(Goals::BUY_ARMY)
|
||||
{}
|
||||
public:
|
||||
BuyArmy(const CGTownInstance * Town, int val)
|
||||
: CGoal(Goals::BUY_ARMY)
|
||||
{
|
||||
town = Town; //where to buy this army
|
||||
value = val; //expressed in AI unit strength
|
||||
priority = 2;//TODO: evaluate?
|
||||
}
|
||||
bool operator==(BuyArmy & g);
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
};
|
||||
|
||||
class DLL_EXPORT BoostHero : public CGoal<BoostHero>
|
||||
{
|
||||
public:
|
||||
BoostHero()
|
||||
@ -324,7 +354,7 @@ public:
|
||||
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
|
||||
};
|
||||
|
||||
class RecruitHero : public CGoal<RecruitHero>
|
||||
class DLL_EXPORT RecruitHero : public CGoal<RecruitHero>
|
||||
{
|
||||
public:
|
||||
RecruitHero()
|
||||
@ -337,37 +367,37 @@ public:
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool operator==(RecruitHero & g);
|
||||
};
|
||||
|
||||
class BuildThis : public CGoal<BuildThis>
|
||||
class DLL_EXPORT BuildThis : public CGoal<BuildThis>
|
||||
{
|
||||
public:
|
||||
BuildThis()
|
||||
BuildThis() //should be private, but unit test uses it
|
||||
: CGoal(Goals::BUILD_STRUCTURE)
|
||||
{
|
||||
//FIXME: should be not allowed (private)
|
||||
}
|
||||
{}
|
||||
BuildThis(BuildingID Bid, const CGTownInstance * tid)
|
||||
: CGoal(Goals::BUILD_STRUCTURE)
|
||||
{
|
||||
bid = Bid;
|
||||
town = tid;
|
||||
priority = 5;
|
||||
priority = 1;
|
||||
}
|
||||
BuildThis(BuildingID Bid)
|
||||
: CGoal(Goals::BUILD_STRUCTURE)
|
||||
{
|
||||
bid = Bid;
|
||||
priority = 5;
|
||||
priority = 1;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//bool fulfillsMe(TSubgoal goal) override;
|
||||
};
|
||||
|
||||
class CollectRes : public CGoal<CollectRes>
|
||||
class DLL_EXPORT CollectRes : public CGoal<CollectRes>
|
||||
{
|
||||
public:
|
||||
CollectRes()
|
||||
@ -381,14 +411,13 @@ public:
|
||||
value = val;
|
||||
priority = 2;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
};
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
TSubgoal whatToDoToTrade();
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
};
|
||||
|
||||
class GatherTroops : public CGoal<GatherTroops>
|
||||
class DLL_EXPORT GatherTroops : public CGoal<GatherTroops>
|
||||
{
|
||||
public:
|
||||
GatherTroops()
|
||||
@ -408,9 +437,10 @@ public:
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
};
|
||||
|
||||
class GetObj : public CGoal<GetObj>
|
||||
class DLL_EXPORT GetObj : public CGoal<GetObj>
|
||||
{
|
||||
public:
|
||||
GetObj() {} // empty constructor not allowed
|
||||
@ -434,7 +464,7 @@ public:
|
||||
std::string completeMessage() const override;
|
||||
};
|
||||
|
||||
class FindObj : public CGoal<FindObj>
|
||||
class DLL_EXPORT FindObj : public CGoal<FindObj>
|
||||
{
|
||||
public:
|
||||
FindObj() {} // empty constructor not allowed
|
||||
@ -443,6 +473,7 @@ public:
|
||||
: CGoal(Goals::FIND_OBJ)
|
||||
{
|
||||
objid = ID;
|
||||
resID = -1; //subid unspecified
|
||||
priority = 1;
|
||||
}
|
||||
FindObj(int ID, int subID)
|
||||
@ -457,9 +488,10 @@ public:
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
};
|
||||
|
||||
class VisitHero : public CGoal<VisitHero>
|
||||
class DLL_EXPORT VisitHero : public CGoal<VisitHero>
|
||||
{
|
||||
public:
|
||||
VisitHero()
|
||||
@ -485,7 +517,7 @@ public:
|
||||
std::string completeMessage() const override;
|
||||
};
|
||||
|
||||
class GetArtOfType : public CGoal<GetArtOfType>
|
||||
class DLL_EXPORT GetArtOfType : public CGoal<GetArtOfType>
|
||||
{
|
||||
public:
|
||||
GetArtOfType()
|
||||
@ -505,7 +537,7 @@ public:
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
};
|
||||
|
||||
class VisitTile : public CGoal<VisitTile>
|
||||
class DLL_EXPORT VisitTile : public CGoal<VisitTile>
|
||||
//tile, in conjunction with hero elementar; assumes tile is reachable
|
||||
{
|
||||
public:
|
||||
@ -526,7 +558,7 @@ public:
|
||||
std::string completeMessage() const override;
|
||||
};
|
||||
|
||||
class ClearWayTo : public CGoal<ClearWayTo>
|
||||
class DLL_EXPORT ClearWayTo : public CGoal<ClearWayTo>
|
||||
{
|
||||
public:
|
||||
ClearWayTo()
|
||||
@ -552,9 +584,10 @@ public:
|
||||
{
|
||||
return g.goalType == goalType && g.tile == tile;
|
||||
}
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
};
|
||||
|
||||
class DigAtTile : public CGoal<DigAtTile>
|
||||
class DLL_EXPORT DigAtTile : public CGoal<DigAtTile>
|
||||
//elementar with hero on tile
|
||||
{
|
||||
public:
|
||||
@ -579,7 +612,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CIssueCommand : public CGoal<CIssueCommand>
|
||||
class DLL_EXPORT CIssueCommand : public CGoal<CIssueCommand>
|
||||
{
|
||||
std::function<bool()> command;
|
||||
|
||||
|
312
AI/VCAI/ResourceManager.cpp
Normal file
312
AI/VCAI/ResourceManager.cpp
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
* 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 "../../CCallback.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
#define GOLD_RESERVE (10000); //at least we'll be able to reach capitol
|
||||
|
||||
ResourceObjective::ResourceObjective(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::setCB(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(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;
|
||||
|
||||
return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
if (!accumulatedResources.canBeAfforded(allResources)) //can't afford
|
||||
return collectResourcesForOurGoal(ro);
|
||||
else //can afford all goals up to this point
|
||||
{
|
||||
if (it->goal == goal)
|
||||
return goal; //can afford immediately
|
||||
}
|
||||
}
|
||||
return collectResourcesForOurGoal(ro); //fallback, ever needed?
|
||||
}
|
||||
|
||||
bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal)
|
||||
{
|
||||
if (goal->invalid())
|
||||
logAi->warn("Attempt to complete Invalid goal");
|
||||
|
||||
bool removedGoal = false;
|
||||
while (true)
|
||||
{ //unfortunatelly we can't use remove_if on heap
|
||||
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
|
||||
{
|
||||
return ro.goal == goal || ro.goal->fulfillsMe (goal);
|
||||
});
|
||||
if (it != queue.end()) //removed at least one
|
||||
{
|
||||
queue.erase(queue.s_handle_from_iterator(it));
|
||||
logAi->debug("Removed goal %s from ResourceManager.", it->goal->name());
|
||||
removedGoal = true;
|
||||
}
|
||||
else //found nothing more to remove
|
||||
return removedGoal;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool ResourceManager::tryPush(ResourceObjective & o)
|
||||
{
|
||||
auto goal = o.goal;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
auto towns = cb->getTownsInfo();
|
||||
if (towns.size()) //we don't save for Capitol if there are no towns
|
||||
{
|
||||
if (std::none_of(towns.begin(), towns.end(), [](const CGTownInstance * x) -> bool
|
||||
{
|
||||
return x->builtBuildings.find(BuildingID::CAPITOL) != x->builtBuildings.end();
|
||||
}))
|
||||
{
|
||||
myRes[Res::GOLD] -= GOLD_RESERVE;
|
||||
//what if capitol is blocked from building in all possessed towns (set in map editor)?
|
||||
}
|
||||
}
|
||||
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];
|
||||
}
|
109
AI/VCAI/ResourceManager.h
Normal file
109
AI/VCAI/ResourceManager.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 "Goals.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(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 IResourceManager //: public: IAbstractManager
|
||||
{
|
||||
public:
|
||||
virtual ~IResourceManager() = default;
|
||||
virtual void setCB(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 hasTasksLeft() const = 0;
|
||||
private:
|
||||
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); //can we afford this goal or need to CollectRes?
|
||||
bool hasTasksLeft() const override;
|
||||
|
||||
protected: //not-const actions only for AI
|
||||
virtual void reserveResoures(TResources &res, Goals::TSubgoal goal = Goals::TSubgoal());
|
||||
virtual bool notifyGoalCompleted(Goals::TSubgoal goal);
|
||||
virtual bool updateGoal(Goals::TSubgoal goal); //new goal must have same properties but different priority
|
||||
virtual bool tryPush(ResourceObjective &o);
|
||||
|
||||
//inner processing
|
||||
virtual TResources estimateIncome() const;
|
||||
virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const;
|
||||
|
||||
void setCB(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
|
||||
private:
|
||||
TResources saving;
|
||||
|
||||
boost::heap::binomial_heap<ResourceObjective> queue;
|
||||
|
||||
//TODO: register?
|
||||
template<typename Handler> void serializeInternal(Handler & h, const int version)
|
||||
{
|
||||
h & saving;
|
||||
h & queue;
|
||||
}
|
||||
};
|
345
AI/VCAI/VCAI.cpp
345
AI/VCAI/VCAI.cpp
@ -10,6 +10,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "VCAI.h"
|
||||
#include "Fuzzy.h"
|
||||
#include "ResourceManager.h"
|
||||
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
@ -22,16 +23,18 @@
|
||||
#include "../../lib/serializer/BinarySerializer.h"
|
||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||
|
||||
#include "AIhelper.h"
|
||||
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
class CGVisitableOPW;
|
||||
|
||||
const double SAFE_ATTACK_CONSTANT = 1.5;
|
||||
const int GOLD_RESERVE = 10000; //when buying creatures we want to keep at least this much gold (10000 so at least we'll be able to reach capitol)
|
||||
|
||||
//one thread may be turn of AI and another will be handling a side effect for AI2
|
||||
boost::thread_specific_ptr<CCallback> cb;
|
||||
boost::thread_specific_ptr<VCAI> ai;
|
||||
extern boost::thread_specific_ptr<AIhelper> ah;
|
||||
|
||||
//std::map<int, std::map<int, int> > HeroView::infosCount;
|
||||
|
||||
@ -45,9 +48,15 @@ struct SetGlobalState
|
||||
|
||||
ai.reset(AI);
|
||||
cb.reset(AI->myCb.get());
|
||||
if (!ah.get())
|
||||
ah.reset(new AIhelper());
|
||||
ah->setAI(AI); //does this make any sense?
|
||||
ah->setCB(cb.get());
|
||||
}
|
||||
~SetGlobalState()
|
||||
{
|
||||
//TODO: how to handle rm? shouldn't be called after ai is destroyed, hopefully
|
||||
//TODO: to ensure that, make rm unique_ptr
|
||||
ai.release();
|
||||
cb.release();
|
||||
}
|
||||
@ -537,6 +546,8 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
|
||||
void VCAI::buildChanged(const CGTownInstance * town, BuildingID buildingID, int what)
|
||||
{
|
||||
LOG_TRACE_PARAMS(logAi, "what '%i'", what);
|
||||
if (town->getOwner() == playerID && what == 1) //built
|
||||
completeGoal(sptr(Goals::BuildThis(buildingID, town)));
|
||||
NET_EVENT_HANDLER;
|
||||
}
|
||||
|
||||
@ -564,7 +575,10 @@ void VCAI::init(std::shared_ptr<CCallback> CB)
|
||||
LOG_TRACE(logAi);
|
||||
myCb = CB;
|
||||
cbc = CB;
|
||||
NET_EVENT_HANDLER;
|
||||
|
||||
ah.reset(new AIhelper());
|
||||
|
||||
NET_EVENT_HANDLER; //sets ah->rm->cb
|
||||
playerID = *myCb->getMyColor();
|
||||
myCb->waitTillRealize = true;
|
||||
myCb->unlockGsWhenWaiting = true;
|
||||
@ -772,8 +786,6 @@ void VCAI::makeTurn()
|
||||
It is not supposed to work this way in final version of VCAI. It consists of few actions/loops done in particular order, hard parts are explained below with focus on explaining hero management logic*/
|
||||
void VCAI::makeTurnInternal()
|
||||
{
|
||||
saving = 0;
|
||||
|
||||
//it looks messy here, but it's better to have armed heroes before attempting realizing goals
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
moveCreaturesToHero(t);
|
||||
@ -814,11 +826,14 @@ void VCAI::makeTurnInternal()
|
||||
/*below line performs goal decomposition, result of the function is ONE goal for ONE hero to realize.*/
|
||||
striveToGoal(sptr(Goals::Win()));
|
||||
|
||||
//TODO: add ResourceManager goals to the pool and process them all at once
|
||||
if (ah->hasTasksLeft())
|
||||
striveToGoal(ah->whatToDo());
|
||||
|
||||
/*Explanation of below loop: At the time of writing this - goals get decomposited either to GatherArmy or Visit Tile.
|
||||
Visit tile that is about visiting object gets processed at beginning of MakeTurnInternal without re-evaluation.
|
||||
Rest of goals that got started via striveToGoal(sptr(Goals::Win())); in previous turns and not finished get continued here.
|
||||
Also they are subject for re-evaluation to see if there is better goal to start (still talking only about heroes that got goals started by via striveToGoal(sptr(Goals::Win())); in previous turns.*/
|
||||
|
||||
//finally, continue our abstract long-term goals
|
||||
int oldMovement = 0;
|
||||
int newMovement = 0;
|
||||
@ -863,6 +878,7 @@ void VCAI::makeTurnInternal()
|
||||
striveToQuest(quest);
|
||||
}
|
||||
|
||||
//TODO: striveToGoal
|
||||
striveToGoal(sptr(Goals::Build())); //TODO: smarter building management
|
||||
|
||||
/*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.
|
||||
@ -914,7 +930,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
||||
if(h->visitedTown) //we are inside, not just attacking
|
||||
{
|
||||
townVisitsThisWeek[h].insert(h->visitedTown);
|
||||
if(!h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST + saving[Res::GOLD])
|
||||
if(!h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
|
||||
{
|
||||
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
|
||||
@ -1150,6 +1166,7 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
|
||||
|
||||
void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter)
|
||||
{
|
||||
//now used only for visited dwellings / towns, not BuyArmy goal
|
||||
for(int i = 0; i < d->creatures.size(); i++)
|
||||
{
|
||||
if(!d->creatures[i].second.size())
|
||||
@ -1157,17 +1174,14 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
|
||||
|
||||
int count = d->creatures[i].first;
|
||||
CreatureID creID = d->creatures[i].second.back();
|
||||
// const CCreature *c = VLC->creh->creatures[creID];
|
||||
// if(containsSavedRes(c->cost))
|
||||
// continue;
|
||||
|
||||
vstd::amin(count, freeResources() / VLC->creh->creatures[creID]->cost);
|
||||
vstd::amin(count, ah->freeResources() / VLC->creh->creatures[creID]->cost);
|
||||
if(count > 0)
|
||||
cb->recruitCreatures(d, recruiter, creID, count, i);
|
||||
}
|
||||
}
|
||||
|
||||
bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays) const
|
||||
bool VCAI::tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays)
|
||||
{
|
||||
if(maxDays == 0)
|
||||
{
|
||||
@ -1199,52 +1213,52 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
|
||||
if(maxDays && toBuild.size() > maxDays)
|
||||
return false;
|
||||
|
||||
TResources currentRes = cb->getResourceAmount();
|
||||
//TODO: calculate if we have enough resources to build it in maxDays
|
||||
//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)
|
||||
if (canBuild == EBuildingState::ALLOWED)
|
||||
{
|
||||
if(!containsSavedRes(b->resources))
|
||||
{
|
||||
logAi->debug("Player %d will build %s in town of %s at %s", playerID, b->Name(), t->name, t->pos.toString());
|
||||
cb->buildBuilding(t, buildID);
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
buildStructure(t, buildID);
|
||||
return true;
|
||||
}
|
||||
else if(canBuild == EBuildingState::NO_RESOURCES)
|
||||
{
|
||||
//We can't do anything about it - no requests from this function
|
||||
continue;
|
||||
}
|
||||
else if(canBuild == EBuildingState::PREREQUIRES)
|
||||
else if (canBuild == EBuildingState::PREREQUIRES)
|
||||
{
|
||||
// can happen when dependencies have their own missing dependencies
|
||||
if(tryBuildStructure(t, buildID, maxDays - 1))
|
||||
if (tryBuildThisStructure(t, buildID, maxDays - 1))
|
||||
return true;
|
||||
}
|
||||
else if(canBuild == EBuildingState::MISSING_BASE)
|
||||
else if (canBuild == EBuildingState::MISSING_BASE)
|
||||
{
|
||||
if(tryBuildStructure(t, b->upgrade, maxDays - 1))
|
||||
return true;
|
||||
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);
|
||||
potentialBuildings.push_back(pb); //these are checked again in try
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VCAI::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays) const
|
||||
bool VCAI::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
|
||||
{
|
||||
for(const auto & building : buildList)
|
||||
{
|
||||
if(t->hasBuilt(building))
|
||||
continue;
|
||||
if(tryBuildStructure(t, building, maxDays))
|
||||
return true;
|
||||
return tryBuildThisStructure(t, building, maxDays);
|
||||
|
||||
}
|
||||
return false; //Can't build anything
|
||||
}
|
||||
@ -1261,17 +1275,24 @@ BuildingID VCAI::canBuildAnyStructure(const CGTownInstance * t, std::vector<Buil
|
||||
return BuildingID::NONE; //Can't build anything
|
||||
}
|
||||
|
||||
bool VCAI::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays) const
|
||||
bool VCAI::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
|
||||
{
|
||||
for(const auto & building : buildList)
|
||||
{
|
||||
if(t->hasBuilt(building))
|
||||
continue;
|
||||
return tryBuildStructure(t, building, maxDays);
|
||||
return tryBuildThisStructure(t, building, maxDays);
|
||||
}
|
||||
return false; //Nothing to build
|
||||
}
|
||||
|
||||
void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
|
||||
{
|
||||
auto name = t->town->buildings.at(building)->Name();
|
||||
logAi->debug("Player %d will build %s in town of %s at %s", playerID, name, t->name, t->pos.toString());
|
||||
cb->buildBuilding(t, building); //just do this;
|
||||
}
|
||||
|
||||
//Set of buildings for different goals. Does not include any prerequisites.
|
||||
static const BuildingID essential[] = {BuildingID::TAVERN, BuildingID::TOWN_HALL};
|
||||
static const BuildingID goldSource[] = {BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL};
|
||||
@ -1287,7 +1308,7 @@ static const BuildingID _spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGE
|
||||
static const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
|
||||
BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings
|
||||
|
||||
void VCAI::buildStructure(const CGTownInstance * t) const
|
||||
bool VCAI::tryBuildStructure(const CGTownInstance * t)
|
||||
{
|
||||
//TODO make *real* town development system
|
||||
//TODO: faction-specific development: use special buildings, build dwellings in better order, etc
|
||||
@ -1299,41 +1320,40 @@ void VCAI::buildStructure(const CGTownInstance * t) const
|
||||
TResources currentIncome = t->dailyIncome();
|
||||
|
||||
if(tryBuildAnyStructure(t, std::vector<BuildingID>(essential, essential + ARRAY_COUNT(essential))))
|
||||
return;
|
||||
return true;
|
||||
|
||||
//the more gold the better and less problems later
|
||||
if(tryBuildNextStructure(t, std::vector<BuildingID>(goldSource, goldSource + ARRAY_COUNT(goldSource))))
|
||||
return;
|
||||
return true;
|
||||
|
||||
//workaround for mantis #2696 - build fort and citadel - building castle will be handled without bug
|
||||
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL)
|
||||
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) &&
|
||||
cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL)
|
||||
{
|
||||
if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
|
||||
{
|
||||
if(tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements, capitolRequirements + ARRAY_COUNT(capitolRequirements))))
|
||||
return;
|
||||
if(tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements,
|
||||
capitolRequirements + ARRAY_COUNT(capitolRequirements))))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//save money for capitol or city hall if capitol unavailable, do not build other things (unless gold source buildings are disabled in map editor)
|
||||
if(!vstd::contains(t->builtBuildings, BuildingID::CAPITOL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
|
||||
return;
|
||||
else if(!vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) == EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
|
||||
return;
|
||||
else if(!vstd::contains(t->builtBuildings, BuildingID::TOWN_HALL) && cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
|
||||
return;
|
||||
//TODO: save money for capitol or city hall if capitol unavailable
|
||||
//do not build other things (unless gold source buildings are disabled in map editor)
|
||||
|
||||
|
||||
if(cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth
|
||||
{
|
||||
if(tryBuildNextStructure(t, std::vector<BuildingID>(unitGrowth, unitGrowth + ARRAY_COUNT(unitGrowth)), 2))
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// first in-game week or second half of any week: try build dwellings
|
||||
if(cb->getDate(Date::DAY) < 7 || cb->getDate(Date::DAY_OF_WEEK) > 3)
|
||||
{
|
||||
if(tryBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)))
|
||||
return;
|
||||
if(tryBuildAnyStructure(t, std::vector<BuildingID>(unitsSource,
|
||||
unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)))
|
||||
return true;
|
||||
}
|
||||
|
||||
//try to upgrade dwelling
|
||||
@ -1341,16 +1361,16 @@ void VCAI::buildStructure(const CGTownInstance * t) const
|
||||
{
|
||||
if(t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]))
|
||||
{
|
||||
if(tryBuildStructure(t, unitsUpgrade[i]))
|
||||
return;
|
||||
if(tryBuildThisStructure(t, unitsUpgrade[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//remaining tasks
|
||||
if(tryBuildNextStructure(t, std::vector<BuildingID>(_spells, _spells + ARRAY_COUNT(_spells))))
|
||||
return;
|
||||
return true;
|
||||
if(tryBuildAnyStructure(t, std::vector<BuildingID>(extra, extra + ARRAY_COUNT(extra))))
|
||||
return;
|
||||
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;
|
||||
@ -1360,7 +1380,9 @@ void VCAI::buildStructure(const CGTownInstance * t) const
|
||||
extraBuildings.push_back(buildingInfo.first);
|
||||
}
|
||||
if(tryBuildAnyStructure(t, extraBuildings))
|
||||
return;
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, SectorMap & sm)
|
||||
@ -1419,7 +1441,7 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
|
||||
t = findTownWithTavern();
|
||||
if(!t)
|
||||
return false;
|
||||
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
|
||||
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager
|
||||
return false;
|
||||
if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES)
|
||||
return false;
|
||||
@ -1602,12 +1624,13 @@ void VCAI::evaluateGoal(HeroPtr h)
|
||||
void VCAI::completeGoal(Goals::TSubgoal goal)
|
||||
{
|
||||
logAi->trace("Completing goal: %s", goal->name());
|
||||
ah->notifyGoalCompleted(goal);
|
||||
if(const CGHeroInstance * h = goal->hero.get(true))
|
||||
{
|
||||
auto it = lockedHeroes.find(h);
|
||||
if(it != lockedHeroes.end())
|
||||
{
|
||||
if(it->second == goal)
|
||||
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
|
||||
@ -1618,7 +1641,7 @@ void VCAI::completeGoal(Goals::TSubgoal goal)
|
||||
{
|
||||
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
|
||||
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;
|
||||
@ -2090,30 +2113,19 @@ void VCAI::tryRealize(Goals::VisitHero & g)
|
||||
|
||||
void VCAI::tryRealize(Goals::BuildThis & g)
|
||||
{
|
||||
const CGTownInstance * t = g.town;
|
||||
auto b = BuildingID(g.bid);
|
||||
auto t = g.town;
|
||||
|
||||
if(!t && g.hero)
|
||||
t = g.hero->visitedTown;
|
||||
|
||||
if(!t)
|
||||
if (t)
|
||||
{
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
|
||||
{
|
||||
switch(cb->canBuildStructure(t, BuildingID(g.bid)))
|
||||
{
|
||||
case EBuildingState::ALLOWED:
|
||||
cb->buildBuilding(t, BuildingID(g.bid));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
logAi->debug("Player %d will build %s in town of %s at %s",
|
||||
playerID, t->town->buildings.at(b)->Name(), t->name, t->pos.toString());
|
||||
cb->buildBuilding(t, b);
|
||||
throw goalFulfilledException(sptr(g));
|
||||
}
|
||||
}
|
||||
else if(cb->canBuildStructure(t, BuildingID(g.bid)) == EBuildingState::ALLOWED)
|
||||
{
|
||||
cb->buildBuilding(t, BuildingID(g.bid));
|
||||
return;
|
||||
}
|
||||
throw cannotFulfillGoalException("Cannot build a given structure!");
|
||||
}
|
||||
|
||||
@ -2132,10 +2144,10 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::CollectRes & g)
|
||||
void VCAI::tryRealize(Goals::CollectRes & g) //trade
|
||||
{
|
||||
if(cb->getResourceAmount(static_cast<Res::ERes>(g.resID)) >= g.value)
|
||||
throw cannotFulfillGoalException("Goal is already fulfilled!");
|
||||
if(ah->freeResources()[g.resID] >= g.value) //goal is already fulfilled. Why we need this chek, anyway?
|
||||
throw goalFulfilledException(sptr(g));
|
||||
|
||||
if(const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid), false))
|
||||
{
|
||||
@ -2143,15 +2155,16 @@ void VCAI::tryRealize(Goals::CollectRes & g)
|
||||
{
|
||||
for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
||||
{
|
||||
if(i == g.resID)
|
||||
if(i == g.resID) //sell any other resource
|
||||
continue;
|
||||
//TODO: check all our reserved resources and avoid them
|
||||
int toGive, toGet;
|
||||
m->getOffer(i, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
|
||||
toGive = toGive * (cb->getResourceAmount(i) / toGive);
|
||||
//TODO trade only as much as needed
|
||||
cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, i, g.resID, toGive);
|
||||
if(cb->getResourceAmount(static_cast<Res::ERes>(g.resID)) >= g.value)
|
||||
return;
|
||||
if (ah->freeResources()[g.resID] >= g.value)
|
||||
throw goalFulfilledException(sptr(g));
|
||||
}
|
||||
|
||||
throw cannotFulfillGoalException("I cannot get needed resources by trade!");
|
||||
@ -2163,28 +2176,87 @@ void VCAI::tryRealize(Goals::CollectRes & g)
|
||||
}
|
||||
else
|
||||
{
|
||||
saving[g.resID] = 1;
|
||||
throw cannotFulfillGoalException("No object that could be used to raise resources!");
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::Build & g)
|
||||
{
|
||||
bool didWeBuildSomething = false;
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
logAi->debug("Looking into %s", t->name);
|
||||
buildStructure(t);
|
||||
|
||||
if(!ai->primaryHero() ||
|
||||
(t->getArmyStrength() > ai->primaryHero()->getArmyStrength() * 2 && !isAccessibleForHero(t->visitablePos(), ai->primaryHero())))
|
||||
potentialBuildings.clear(); //start fresh with every town
|
||||
if (tryBuildStructure(t))
|
||||
didWeBuildSomething = true;
|
||||
else if (potentialBuildings.size())
|
||||
{
|
||||
recruitHero(t);
|
||||
buildArmyIn(t);
|
||||
auto pb = potentialBuildings.front(); //gather resources for any we can't afford
|
||||
auto goal = ah->whatToDo(pb.price, sptr(Goals::BuildThis(pb.bid, t)));
|
||||
if (goal->goalType == Goals::BUILD_STRUCTURE)
|
||||
{
|
||||
logAi->error("We were supposed to NOT afford any building");
|
||||
buildStructure(t, pb.bid); //do it right now
|
||||
didWeBuildSomething = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: right now we do that for every town in order. Consider comparison of all potential goals.
|
||||
striveToGoal(goal); //gather resources, or something else?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
|
||||
if (!didWeBuildSomething)
|
||||
throw cannotFulfillGoalException("BUILD has been realized as much as possible."); //who catches it and what for?
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::BuyArmy & g)
|
||||
{
|
||||
auto t = g.town;
|
||||
|
||||
ui64 valueBought = 0;
|
||||
//buy the stacks with largest AI value
|
||||
|
||||
while (valueBought < g.value)
|
||||
{
|
||||
auto res = ah->allResources();
|
||||
std::vector<creInfo> creaturesInDwellings;
|
||||
for (int i = 0; i < t->creatures.size(); i++)
|
||||
{
|
||||
auto ci = infoFromDC(t->creatures[i]);
|
||||
ci.level = i; //this is important for Dungeon Summoning Portal
|
||||
creaturesInDwellings.push_back(ci);
|
||||
}
|
||||
vstd::erase_if(creaturesInDwellings, [](const creInfo & ci) -> bool
|
||||
{
|
||||
return !ci.count || ci.creID == -1;
|
||||
});
|
||||
if (creaturesInDwellings.empty())
|
||||
throw cannotFulfillGoalException("Can't buy any more creatures!");
|
||||
|
||||
creInfo ci =
|
||||
*boost::max_element(creaturesInDwellings, [&res](const creInfo & lhs, const creInfo & rhs)
|
||||
{
|
||||
//max value of creatures we can buy with our res
|
||||
int value1 = lhs.cre->AIValue * (std::min(lhs.count, res / lhs.cre->cost)),
|
||||
value2 = rhs.cre->AIValue * (std::min(rhs.count, res / rhs.cre->cost));
|
||||
|
||||
return value1 < value2;
|
||||
});
|
||||
|
||||
vstd::amin(ci.count, res / ci.cre->cost); //max count we can afford
|
||||
if (ci.count > 0)
|
||||
{
|
||||
cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
|
||||
valueBought += ci.count * ci.cre->AIValue;
|
||||
}
|
||||
else
|
||||
throw cannotFulfillGoalException("Can't buy any more creatures!");
|
||||
}
|
||||
throw goalFulfilledException(sptr(g)); //we bought as many creatures as we wanted
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::Invalid & g)
|
||||
{
|
||||
throw cannotFulfillGoalException("I don't know how to fulfill this!");
|
||||
@ -2303,7 +2375,7 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
|
||||
boost::this_thread::interruption_point();
|
||||
goal = goal->whatToDoToAchieve();
|
||||
--maxGoals;
|
||||
if(*goal == *ultimateGoal) //compare objects by value
|
||||
if(goal == ultimateGoal) //compare objects by value
|
||||
throw cannotFulfillGoalException("Goal dependency loop detected!");
|
||||
}
|
||||
catch(goalFulfilledException & e)
|
||||
@ -2319,7 +2391,6 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::this_thread::interruption_point();
|
||||
@ -2328,10 +2399,10 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
|
||||
{
|
||||
if(ultimateGoal->hero) // we seemingly don't know what to do with hero, free him
|
||||
vstd::erase_if_present(lockedHeroes, ultimateGoal->hero);
|
||||
std::runtime_error e("Too many subgoals, don't know what to do");
|
||||
throw (e);
|
||||
throw (std::runtime_error("Too many subgoals, don't know what to do"));
|
||||
|
||||
}
|
||||
else //we can proceed
|
||||
else //we found elementar goal and can proceed
|
||||
{
|
||||
if(goal->hero) //lock this hero to fulfill ultimate goal
|
||||
{
|
||||
@ -2345,7 +2416,7 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
|
||||
logAi->debug("Choosing abstract goal %s", goal->name());
|
||||
break;
|
||||
}
|
||||
else
|
||||
else //try realize
|
||||
{
|
||||
logAi->debug("Trying to realize %s (value %2.3f)", goal->name(), goal->priority);
|
||||
goal->accept(this);
|
||||
@ -2360,7 +2431,9 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
|
||||
}
|
||||
catch(goalFulfilledException & e)
|
||||
{
|
||||
//the goal was completed successfully
|
||||
//the sub-goal was completed successfully
|
||||
completeGoal(e.goal);
|
||||
//local goal was also completed... TODO: or not?
|
||||
completeGoal(goal);
|
||||
//completed goal was main goal //TODO: find better condition
|
||||
if(ultimateGoal->fulfillsMe(goal) || maxGoals > searchDepth2)
|
||||
@ -2515,11 +2588,6 @@ void VCAI::striveToQuest(const QuestInfo & q)
|
||||
|
||||
void VCAI::performTypicalActions()
|
||||
{
|
||||
//TODO: build army only on request
|
||||
for(auto t : cb->getTownsInfo())
|
||||
{
|
||||
buildArmyIn(t);
|
||||
}
|
||||
for(auto h : getUnblockedHeroes())
|
||||
{
|
||||
if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
|
||||
@ -2688,49 +2756,6 @@ int3 VCAI::explorationDesperate(HeroPtr h)
|
||||
return bestTile;
|
||||
}
|
||||
|
||||
TResources VCAI::estimateIncome() const
|
||||
{
|
||||
TResources ret;
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
ret += t->dailyIncome();
|
||||
}
|
||||
|
||||
for(const CGObjectInstance * obj : 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;
|
||||
}
|
||||
|
||||
bool VCAI::containsSavedRes(const TResources & cost) const
|
||||
{
|
||||
for(int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
|
||||
{
|
||||
if(saving[i] && cost[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void VCAI::checkHeroArmy(HeroPtr h)
|
||||
{
|
||||
auto it = lockedHeroes.find(h);
|
||||
@ -2755,6 +2780,7 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
|
||||
hero = heroes[1];
|
||||
}
|
||||
cb->recruitHero(t, hero);
|
||||
throw goalFulfilledException(sptr(Goals::RecruitHero().settown(t)));
|
||||
}
|
||||
else if(throwing)
|
||||
{
|
||||
@ -2849,20 +2875,6 @@ void VCAI::validateObject(ObjectIdRef obj)
|
||||
}
|
||||
}
|
||||
|
||||
TResources VCAI::freeResources() const
|
||||
{
|
||||
TResources myRes = cb->getResourceAmount();
|
||||
auto iterator = cb->getTownsInfo();
|
||||
if(std::none_of(iterator.begin(), iterator.end(), [](const CGTownInstance * x) -> bool
|
||||
{
|
||||
return x->builtBuildings.find(BuildingID::CAPITOL) != x->builtBuildings.end();
|
||||
})
|
||||
/*|| std::all_of(iterator.begin(), iterator.end(), [](const CGTownInstance * x) -> bool { return x->forbiddenBuildings.find(BuildingID::CAPITOL) != x->forbiddenBuildings.end(); })*/ )
|
||||
myRes[Res::GOLD] -= GOLD_RESERVE; //what if capitol is blocked from building in all possessed towns (set in map editor)? What about reserve for city hall or something similar in that case?
|
||||
vstd::amax(myRes[Res::GOLD], 0);
|
||||
return myRes;
|
||||
}
|
||||
|
||||
std::shared_ptr<SectorMap> VCAI::getCachedSectorMap(HeroPtr h)
|
||||
{
|
||||
auto it = cachedSectorMaps.find(h);
|
||||
@ -3271,8 +3283,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||
case Obj::SCHOOL_OF_MAGIC:
|
||||
case Obj::SCHOOL_OF_WAR:
|
||||
{
|
||||
TResources myRes = ai->myCb->getResourceAmount();
|
||||
if(myRes[Res::GOLD] - GOLD_RESERVE < 1000)
|
||||
if (ah->freeGold() < 1000)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -3282,8 +3293,8 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||
break;
|
||||
case Obj::TREE_OF_KNOWLEDGE:
|
||||
{
|
||||
TResources myRes = ai->myCb->getResourceAmount();
|
||||
if(myRes[Res::GOLD] - GOLD_RESERVE < 2000 || myRes[Res::GEMS] < 10)
|
||||
TResources myRes = ah->freeResources();
|
||||
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -3297,7 +3308,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||
//TODO: only on request
|
||||
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
|
||||
return false;
|
||||
else if(ai->myCb->getResourceAmount()[Res::GOLD] - GOLD_RESERVE < GameConstants::HERO_GOLD_COST)
|
||||
else if(ah->freeGold() < GameConstants::HERO_GOLD_COST)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
154
AI/VCAI/VCAI.h
154
AI/VCAI/VCAI.h
@ -126,21 +126,33 @@ struct SectorMap
|
||||
int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
|
||||
};
|
||||
|
||||
class VCAI : public CAdventureAI
|
||||
class DLL_EXPORT VCAI : public CAdventureAI
|
||||
{
|
||||
public:
|
||||
//internal methods for town development
|
||||
//TODO: refactor to separate class BuildManager
|
||||
|
||||
//try build an unbuilt structure in maxDays at most (0 = indefinite)
|
||||
/*bool canBuildStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays=7);*/
|
||||
bool tryBuildStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7) const;
|
||||
//try build anything in given town, and execute resulting Goal if any
|
||||
bool tryBuildStructure(const CGTownInstance * t);
|
||||
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
|
||||
BuildingID canBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7) const;
|
||||
bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7) const;
|
||||
//try build first unbuilt structure
|
||||
bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7) const;
|
||||
bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
|
||||
void buildStructure(const CGTownInstance * t, BuildingID building); //actually execute build operation
|
||||
|
||||
struct PotentialBuilding
|
||||
{
|
||||
BuildingID bid;
|
||||
TResources price;
|
||||
//days to build?
|
||||
};
|
||||
std::vector<PotentialBuilding> potentialBuildings; //what we can build in current town
|
||||
|
||||
friend class FuzzyHelper;
|
||||
friend class ResourceManager;
|
||||
|
||||
std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
|
||||
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
|
||||
@ -161,8 +173,6 @@ public:
|
||||
|
||||
std::map<HeroPtr, std::shared_ptr<SectorMap>> cachedSectorMaps; //TODO: serialize? not necessary
|
||||
|
||||
TResources saving;
|
||||
|
||||
AIStatus status;
|
||||
std::string battlename;
|
||||
|
||||
@ -182,6 +192,7 @@ public:
|
||||
void tryRealize(Goals::DigAtTile & g);
|
||||
void tryRealize(Goals::CollectRes & g);
|
||||
void tryRealize(Goals::Build & g);
|
||||
void tryRealize(Goals::BuyArmy & g);
|
||||
void tryRealize(Goals::Invalid & g);
|
||||
void tryRealize(Goals::AbstractGoal & g);
|
||||
|
||||
@ -189,71 +200,70 @@ public:
|
||||
int3 explorationNewPoint(HeroPtr h);
|
||||
int3 explorationDesperate(HeroPtr h);
|
||||
bool isTileNotReserved(const CGHeroInstance * h, int3 t); //the tile is not occupied by allied hero and the object is not reserved
|
||||
void recruitHero();
|
||||
|
||||
virtual std::string getBattleAIName() const override;
|
||||
std::string getBattleAIName() const override;
|
||||
|
||||
virtual void init(std::shared_ptr<CCallback> CB) override;
|
||||
virtual void yourTurn() override;
|
||||
void init(std::shared_ptr<CCallback> CB) override;
|
||||
void yourTurn() override;
|
||||
|
||||
virtual void heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
|
||||
virtual void commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
|
||||
virtual void showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
|
||||
virtual void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
|
||||
virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
|
||||
void heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
|
||||
void commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
|
||||
void showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
|
||||
void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
|
||||
void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
|
||||
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
|
||||
virtual void saveGame(BinarySerializer & h, const int version) override; //saving
|
||||
virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading
|
||||
virtual void finish() override;
|
||||
void saveGame(BinarySerializer & h, const int version) override; //saving
|
||||
void loadGame(BinaryDeserializer & h, const int version) override; //loading
|
||||
void finish() override;
|
||||
|
||||
virtual void availableCreaturesChanged(const CGDwelling * town) override;
|
||||
virtual void heroMoved(const TryMoveHero & details) override;
|
||||
virtual void heroInGarrisonChange(const CGTownInstance * town) override;
|
||||
virtual void centerView(int3 pos, int focusTime) override;
|
||||
virtual void tileHidden(const std::unordered_set<int3, ShashInt3> & pos) override;
|
||||
virtual void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
|
||||
virtual void artifactAssembled(const ArtifactLocation & al) override;
|
||||
virtual void showTavernWindow(const CGObjectInstance * townOrTavern) override;
|
||||
virtual void showThievesGuildWindow(const CGObjectInstance * obj) override;
|
||||
virtual void playerBlocked(int reason, bool start) override;
|
||||
virtual void showPuzzleMap() override;
|
||||
virtual void showShipyardDialog(const IShipyard * obj) override;
|
||||
virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
|
||||
virtual void artifactPut(const ArtifactLocation & al) override;
|
||||
virtual void artifactRemoved(const ArtifactLocation & al) override;
|
||||
virtual void artifactDisassembled(const ArtifactLocation & al) override;
|
||||
virtual void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
|
||||
virtual void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
|
||||
virtual void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
|
||||
virtual void tileRevealed(const std::unordered_set<int3, ShashInt3> & pos) override;
|
||||
virtual void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
|
||||
virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
|
||||
virtual void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
|
||||
virtual void heroMovePointsChanged(const CGHeroInstance * hero) override;
|
||||
virtual void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
|
||||
virtual void newObject(const CGObjectInstance * obj) override;
|
||||
virtual void showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor) override;
|
||||
virtual void playerBonusChanged(const Bonus & bonus, bool gain) override;
|
||||
virtual void heroCreated(const CGHeroInstance *) override;
|
||||
virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID) override;
|
||||
virtual void showInfoDialog(const std::string & text, const std::vector<Component> & components, int soundID) override;
|
||||
virtual void requestRealized(PackageApplied * pa) override;
|
||||
virtual void receivedResource() override;
|
||||
virtual void objectRemoved(const CGObjectInstance * obj) override;
|
||||
virtual void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
|
||||
virtual void heroManaPointsChanged(const CGHeroInstance * hero) override;
|
||||
virtual void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
|
||||
virtual void battleResultsApplied() override;
|
||||
virtual void objectPropertyChanged(const SetObjectProperty * sop) override;
|
||||
virtual void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
|
||||
virtual void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
|
||||
virtual void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
|
||||
void availableCreaturesChanged(const CGDwelling * town) override;
|
||||
void heroMoved(const TryMoveHero & details) override;
|
||||
void heroInGarrisonChange(const CGTownInstance * town) override;
|
||||
void centerView(int3 pos, int focusTime) override;
|
||||
void tileHidden(const std::unordered_set<int3, ShashInt3> & pos) override;
|
||||
void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
|
||||
void artifactAssembled(const ArtifactLocation & al) override;
|
||||
void showTavernWindow(const CGObjectInstance * townOrTavern) override;
|
||||
void showThievesGuildWindow(const CGObjectInstance * obj) override;
|
||||
void playerBlocked(int reason, bool start) override;
|
||||
void showPuzzleMap() override;
|
||||
void showShipyardDialog(const IShipyard * obj) override;
|
||||
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
|
||||
void artifactPut(const ArtifactLocation & al) override;
|
||||
void artifactRemoved(const ArtifactLocation & al) override;
|
||||
void artifactDisassembled(const ArtifactLocation & al) override;
|
||||
void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
|
||||
void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
|
||||
void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
|
||||
void tileRevealed(const std::unordered_set<int3, ShashInt3> & pos) override;
|
||||
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
|
||||
void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
|
||||
void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
|
||||
void heroMovePointsChanged(const CGHeroInstance * hero) override;
|
||||
void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
|
||||
void newObject(const CGObjectInstance * obj) override;
|
||||
void showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor) override;
|
||||
void playerBonusChanged(const Bonus & bonus, bool gain) override;
|
||||
void heroCreated(const CGHeroInstance *) override;
|
||||
void advmapSpellCast(const CGHeroInstance * caster, int spellID) override;
|
||||
void showInfoDialog(const std::string & text, const std::vector<Component> & components, int soundID) override;
|
||||
void requestRealized(PackageApplied * pa) override;
|
||||
void receivedResource() override;
|
||||
void objectRemoved(const CGObjectInstance * obj) override;
|
||||
void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
|
||||
void heroManaPointsChanged(const CGHeroInstance * hero) override;
|
||||
void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
|
||||
void battleResultsApplied() override;
|
||||
void objectPropertyChanged(const SetObjectProperty * sop) override;
|
||||
void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
|
||||
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
|
||||
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
|
||||
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
|
||||
|
||||
virtual void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
|
||||
virtual void battleEnd(const BattleResult * br) override;
|
||||
void makeTurn();
|
||||
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
|
||||
void battleEnd(const BattleResult * br) override;
|
||||
|
||||
void makeTurn();
|
||||
void makeTurnInternal();
|
||||
void performTypicalActions();
|
||||
|
||||
@ -269,7 +279,6 @@ public:
|
||||
|
||||
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, SectorMap & sm);
|
||||
void buildStructure(const CGTownInstance * t) const;
|
||||
//void recruitCreatures(const CGTownInstance * t);
|
||||
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
|
||||
bool canGetArmy(const CGHeroInstance * h, const CGHeroInstance * source); //can we get any better stacks from other hero?
|
||||
@ -299,7 +308,7 @@ public:
|
||||
void validateVisitableObjs();
|
||||
void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
|
||||
void retrieveVisitableObjs();
|
||||
std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
||||
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
||||
|
||||
const CGObjectInstance * lookForArt(int aid) const;
|
||||
bool isAccessible(const int3 & pos);
|
||||
@ -317,9 +326,6 @@ public:
|
||||
bool canAct(HeroPtr h) const;
|
||||
std::vector<HeroPtr> getUnblockedHeroes() const;
|
||||
HeroPtr primaryHero() const;
|
||||
TResources freeResources() const; //owned resources minus gold reserve
|
||||
TResources estimateIncome() const;
|
||||
bool containsSavedRes(const TResources & cost) const;
|
||||
void checkHeroArmy(HeroPtr h);
|
||||
|
||||
void requestSent(const CPackForServer * pack, int requestID) override;
|
||||
@ -406,7 +412,11 @@ public:
|
||||
h & visitableObjs;
|
||||
h & alreadyVisited;
|
||||
h & reservedObjs;
|
||||
h & saving;
|
||||
if (version < 788 && !h.saving)
|
||||
{
|
||||
TResources saving;
|
||||
h & saving; //mind the ambiguity
|
||||
}
|
||||
h & status;
|
||||
h & battlename;
|
||||
h & heroesUnableToExplore;
|
||||
|
@ -50,102 +50,102 @@ protected:
|
||||
|
||||
public:
|
||||
//various
|
||||
int getDate(Date::EDateType mode=Date::DAY)const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
|
||||
const StartInfo * getStartInfo(bool beforeRandomization = false)const;
|
||||
bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact; 2 - secondary skill
|
||||
virtual int getDate(Date::EDateType mode=Date::DAY)const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
|
||||
virtual const StartInfo * getStartInfo(bool beforeRandomization = false)const;
|
||||
virtual bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact; 2 - secondary skill
|
||||
|
||||
//player
|
||||
const PlayerState * getPlayer(PlayerColor color, bool verbose = true) const;
|
||||
int getResource(PlayerColor Player, Res::ERes which) const;
|
||||
bool isVisible(int3 pos) const;
|
||||
PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const;
|
||||
void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object
|
||||
EPlayerStatus::EStatus getPlayerStatus(PlayerColor player, bool verbose = true) const; //-1 if no such player
|
||||
PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns
|
||||
virtual const PlayerState * getPlayer(PlayerColor color, bool verbose = true) const;
|
||||
virtual int getResource(PlayerColor Player, Res::ERes which) const;
|
||||
virtual bool isVisible(int3 pos) const;
|
||||
virtual PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const;
|
||||
virtual void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object
|
||||
virtual EPlayerStatus::EStatus getPlayerStatus(PlayerColor player, bool verbose = true) const; //-1 if no such player
|
||||
virtual PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns
|
||||
virtual PlayerColor getLocalPlayer() const; //player that is currently owning given client (if not a client, then returns current player)
|
||||
const PlayerSettings * getPlayerSettings(PlayerColor color) const;
|
||||
virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const;
|
||||
|
||||
|
||||
//armed object
|
||||
void getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out)const;
|
||||
virtual void getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out)const;
|
||||
|
||||
//hero
|
||||
const CGHeroInstance* getHero(ObjectInstanceID objid) const;
|
||||
const CGHeroInstance* getHeroWithSubid(int subid) const;
|
||||
int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
|
||||
bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||
int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
|
||||
int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
|
||||
const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
|
||||
const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
|
||||
const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const;
|
||||
virtual const CGHeroInstance* getHero(ObjectInstanceID objid) const;
|
||||
virtual const CGHeroInstance* getHeroWithSubid(int subid) const;
|
||||
virtual int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
|
||||
virtual bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||
virtual int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
|
||||
virtual int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
|
||||
virtual const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
|
||||
virtual const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
|
||||
//virtual const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const;
|
||||
|
||||
//objects
|
||||
const CGObjectInstance* getObj(ObjectInstanceID objid, bool verbose = true) const;
|
||||
std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
|
||||
std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true)const;
|
||||
std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
|
||||
const CGObjectInstance * getTopObj (int3 pos) const;
|
||||
PlayerColor getOwner(ObjectInstanceID heroID) const;
|
||||
const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)
|
||||
virtual const CGObjectInstance* getObj(ObjectInstanceID objid, bool verbose = true) const;
|
||||
virtual std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
|
||||
virtual std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true)const;
|
||||
virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
|
||||
virtual const CGObjectInstance * getTopObj (int3 pos) const;
|
||||
virtual PlayerColor getOwner(ObjectInstanceID heroID) const;
|
||||
virtual const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)
|
||||
|
||||
//map
|
||||
int3 guardingCreaturePosition (int3 pos) const;
|
||||
std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const;
|
||||
const CMapHeader * getMapHeader()const;
|
||||
int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
|
||||
const TerrainTile * getTile(int3 tile, bool verbose = true) const;
|
||||
std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
|
||||
bool isInTheMap(const int3 &pos) const;
|
||||
void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
|
||||
virtual int3 guardingCreaturePosition (int3 pos) const;
|
||||
virtual std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const;
|
||||
virtual const CMapHeader * getMapHeader()const;
|
||||
virtual int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
|
||||
virtual const TerrainTile * getTile(int3 tile, bool verbose = true) const;
|
||||
virtual std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
|
||||
virtual bool isInTheMap(const int3 &pos) const;
|
||||
virtual void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
|
||||
|
||||
//town
|
||||
const CGTownInstance* getTown(ObjectInstanceID objid) const;
|
||||
int howManyTowns(PlayerColor Player) const;
|
||||
const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial)
|
||||
std::vector<const CGHeroInstance *> getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited
|
||||
std::string getTavernRumor(const CGObjectInstance * townOrTavern) const;
|
||||
EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
|
||||
virtual const CGTownInstance* getTown(ObjectInstanceID objid) const;
|
||||
virtual int howManyTowns(PlayerColor Player) const;
|
||||
//virtual const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial)
|
||||
virtual std::vector<const CGHeroInstance *> getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited
|
||||
virtual std::string getTavernRumor(const CGObjectInstance * townOrTavern) const;
|
||||
virtual EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
|
||||
virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||
const CTown *getNativeTown(PlayerColor color) const;
|
||||
virtual const CTown *getNativeTown(PlayerColor color) const;
|
||||
|
||||
//from gs
|
||||
const TeamState *getTeam(TeamID teamID) const;
|
||||
const TeamState *getPlayerTeam(PlayerColor color) const;
|
||||
EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
|
||||
virtual const TeamState *getTeam(TeamID teamID) const;
|
||||
virtual const TeamState *getPlayerTeam(PlayerColor color) const;
|
||||
//virtual EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
|
||||
|
||||
//teleport
|
||||
std::vector<ObjectInstanceID> getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const;
|
||||
std::vector<ObjectInstanceID> getTeleportChannelEntraces(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
|
||||
std::vector<ObjectInstanceID> getTeleportChannelExits(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
|
||||
ETeleportChannelType getTeleportChannelType(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
bool isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
bool isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const;
|
||||
virtual std::vector<ObjectInstanceID> getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const;
|
||||
virtual std::vector<ObjectInstanceID> getTeleportChannelEntraces(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
|
||||
virtual std::vector<ObjectInstanceID> getTeleportChannelExits(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
|
||||
virtual ETeleportChannelType getTeleportChannelType(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
virtual bool isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
virtual bool isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
virtual bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||
virtual bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback
|
||||
{
|
||||
public:
|
||||
int howManyTowns() const;
|
||||
int howManyHeroes(bool includeGarrisoned = true) const;
|
||||
int3 getGrailPos(double *outKnownRatio);
|
||||
boost::optional<PlayerColor> getMyColor() const;
|
||||
virtual int howManyTowns() const;
|
||||
virtual int howManyHeroes(bool includeGarrisoned = true) const;
|
||||
virtual int3 getGrailPos(double *outKnownRatio);
|
||||
virtual boost::optional<PlayerColor> getMyColor() const;
|
||||
|
||||
std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
|
||||
int getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned=true) const;
|
||||
const CGTownInstance* getTownBySerial(int serialId) const; // serial id is [0, number of towns)
|
||||
const CGHeroInstance* getHeroBySerial(int serialId, bool includeGarrisoned=true) const; // serial id is [0, number of heroes)
|
||||
std::vector <const CGHeroInstance *> getHeroesInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
|
||||
std::vector <const CGDwelling *> getMyDwellings() const; //returns all dwellings that belong to player
|
||||
std::vector <const CGObjectInstance * > getMyObjects() const; //returns all objects flagged by belonging player
|
||||
std::vector <QuestInfo> getMyQuests() const;
|
||||
virtual std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
|
||||
virtual int getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned=true) const;
|
||||
virtual const CGTownInstance* getTownBySerial(int serialId) const; // serial id is [0, number of towns)
|
||||
virtual const CGHeroInstance* getHeroBySerial(int serialId, bool includeGarrisoned=true) const; // serial id is [0, number of heroes)
|
||||
virtual std::vector <const CGHeroInstance *> getHeroesInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
|
||||
virtual std::vector <const CGDwelling *> getMyDwellings() const; //returns all dwellings that belong to player
|
||||
virtual std::vector <const CGObjectInstance * > getMyObjects() const; //returns all objects flagged by belonging player
|
||||
virtual std::vector <QuestInfo> getMyQuests() const;
|
||||
|
||||
int getResourceAmount(Res::ERes type) const;
|
||||
TResources getResourceAmount() const;
|
||||
const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map
|
||||
const PlayerSettings * getPlayerSettings(PlayerColor color) const;
|
||||
virtual int getResourceAmount(Res::ERes type) const;
|
||||
virtual TResources getResourceAmount() const;
|
||||
virtual const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map
|
||||
//virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE IGameEventRealizer
|
||||
|
@ -28,6 +28,21 @@ Res::ResourceSet::ResourceSet(const JsonNode & node)
|
||||
push_back(node[name].Float());
|
||||
}
|
||||
|
||||
Res::ResourceSet::ResourceSet(TResource wood, TResource mercury, TResource ore, TResource sulfur, TResource crystal,
|
||||
TResource gems, TResource gold, TResource mithril)
|
||||
{
|
||||
resize(GameConstants::RESOURCE_QUANTITY);
|
||||
auto d = data();
|
||||
d[Res::WOOD] = wood;
|
||||
d[Res::MERCURY] = mercury;
|
||||
d[Res::ORE] = ore;
|
||||
d[Res::SULFUR] = sulfur;
|
||||
d[Res::CRYSTAL] = crystal;
|
||||
d[Res::GEMS] = gems;
|
||||
d[Res::GOLD] = gold;
|
||||
d[Res::MITHRIL] = mithril;
|
||||
}
|
||||
|
||||
void Res::ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
|
||||
{
|
||||
if(!handler.saving)
|
||||
|
@ -25,7 +25,8 @@ namespace Res
|
||||
{
|
||||
WOOD = 0, MERCURY, ORE, SULFUR, CRYSTAL, GEMS, GOLD, MITHRIL,
|
||||
|
||||
WOOD_AND_ORE = 127 // special case for town bonus resource
|
||||
WOOD_AND_ORE = 127, // special case for town bonus resource
|
||||
INVALID = -1
|
||||
};
|
||||
|
||||
//class to be representing a vector of resource
|
||||
@ -35,6 +36,8 @@ namespace Res
|
||||
DLL_LINKAGE ResourceSet();
|
||||
// read resources set from json. Format example: { "gold": 500, "wood":5 }
|
||||
DLL_LINKAGE ResourceSet(const JsonNode & node);
|
||||
DLL_LINKAGE ResourceSet(TResource wood, TResource mercury, TResource ore, TResource sulfur, TResource crystal,
|
||||
TResource gems, TResource gold, TResource mithril = 0);
|
||||
|
||||
|
||||
#define scalarOperator(OPSIGN) \
|
||||
|
@ -1262,6 +1262,18 @@ bool CGTownInstance::hasBuilt(BuildingID buildingID, int townID) const
|
||||
return false;
|
||||
}
|
||||
|
||||
TResources CGTownInstance::getBuildingCost(BuildingID buildingID) const
|
||||
{
|
||||
if (vstd::contains(town->buildings, buildingID))
|
||||
return town->buildings.at(buildingID)->resources;
|
||||
else
|
||||
{
|
||||
logGlobal->error("Town %s at %s has no possible building %d!", name, pos.toString(), buildingID.toEnum());
|
||||
return TResources();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CGTownInstance::hasBuilt(BuildingID buildingID) const
|
||||
{
|
||||
return vstd::contains(builtBuildings, buildingID);
|
||||
|
@ -265,6 +265,7 @@ public:
|
||||
//checks if building is constructed and town has same subID
|
||||
bool hasBuilt(BuildingID buildingID) const;
|
||||
bool hasBuilt(BuildingID buildingID, int townID) const;
|
||||
TResources getBuildingCost(BuildingID buildingID) const;
|
||||
TResources dailyIncome() const; //calculates daily income of this town
|
||||
int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
|
||||
bool armedGarrison() const; //true if town has creatures in garrison or garrisoned hero
|
||||
|
@ -137,7 +137,7 @@ EMonsterStrength::EMonsterStrength CMapGenOptions::getMonsterStrength() const
|
||||
|
||||
void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value)
|
||||
{
|
||||
monsterStrength = value;
|
||||
monsterStrength = value;
|
||||
}
|
||||
|
||||
void CMapGenOptions::resetPlayersMap()
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 787;
|
||||
const ui32 SERIALIZATION_VERSION = 788;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
13
test/mock/mock_CPSICallback.cpp
Normal file
13
test/mock/mock_CPSICallback.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* {file}.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 "mock_CPSICallback.h"
|
28
test/mock/mock_CPSICallback.h
Normal file
28
test/mock/mock_CPSICallback.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* mock_CPLayerSpecificInfoCallback.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 "gtest/gtest.h"
|
||||
|
||||
#include "../lib/CGameInfoCallback.h"
|
||||
#include "../lib/ResourceSet.h"
|
||||
|
||||
class CCallback;
|
||||
|
||||
class GameCallbackMock : public CPlayerSpecificInfoCallback
|
||||
{
|
||||
public:
|
||||
using CPlayerSpecificInfoCallback::CPlayerSpecificInfoCallback;
|
||||
|
||||
MOCK_CONST_METHOD0(getResourceAmount, TResources());
|
||||
//std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const;
|
||||
MOCK_CONST_METHOD0(getTownsInfo, std::vector <const CGTownInstance *>());
|
||||
MOCK_CONST_METHOD1(getTownsInfo, std::vector <const CGTownInstance *>(bool));
|
||||
};
|
13
test/vcai/ResourceManagerTest.h
Normal file
13
test/vcai/ResourceManagerTest.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* ResourceManagerTest.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
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
254
test/vcai/ResurceManagerTest.cpp
Normal file
254
test/vcai/ResurceManagerTest.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* ResourceManagerTest.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 "gtest/gtest.h"
|
||||
|
||||
#include "../AI/VCAI/VCAI.h"
|
||||
#include "ResourceManagerTest.h"
|
||||
#include "../AI/VCAI/Goals.h"
|
||||
#include "mock_VCAI_CGoal.h"
|
||||
#include "mock_VCAI.h"
|
||||
#include "mock_ResourceManager.h"
|
||||
#include "../mock/mock_CPSICallback.h"
|
||||
#include "../lib/CGameInfoCallback.h"
|
||||
|
||||
using namespace Goals;
|
||||
using namespace ::testing;
|
||||
|
||||
struct ResourceManagerTest : public Test//, public IResourceManager
|
||||
{
|
||||
std::unique_ptr<ResourceManagerMock> rm;
|
||||
|
||||
NiceMock<GameCallbackMock> gcm;
|
||||
NiceMock<VCAIMock> aim;
|
||||
TSubgoal invalidGoal, gatherArmy, buildThis, buildAny, recruitHero;
|
||||
|
||||
ResourceManagerTest()
|
||||
{
|
||||
rm = std::make_unique<NiceMock<ResourceManagerMock>>(&gcm, &aim);
|
||||
|
||||
//note: construct new goal for modfications
|
||||
invalidGoal = sptr(StrictMock<InvalidGoalMock>());
|
||||
gatherArmy = sptr(StrictMock<GatherArmyGoalMock>());
|
||||
buildThis = sptr(StrictMock<BuildThis>());
|
||||
buildAny = sptr(StrictMock<Build>());
|
||||
recruitHero = sptr(StrictMock<RecruitHero>());
|
||||
|
||||
//auto AI = CDynLibHandler::getNewAI("VCAI.dll");
|
||||
//SET_GLOBAL_STATE(AI);
|
||||
|
||||
//gtest couldn't deduce default return value;
|
||||
ON_CALL(gcm, getTownsInfo(_))
|
||||
.WillByDefault(Return(std::vector < const CGTownInstance *>()));
|
||||
|
||||
ON_CALL(gcm, getTownsInfo())
|
||||
.WillByDefault(Return(std::vector < const CGTownInstance *>()));
|
||||
|
||||
ON_CALL(aim, getFlaggedObjects())
|
||||
.WillByDefault(Return(std::vector < const CGObjectInstance *>()));
|
||||
|
||||
//enable if get unexpected exceptions
|
||||
//ON_CALL(gcm, getResourceAmount())
|
||||
// .WillByDefault(Return(TResources()));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ResourceManagerTest, canAffordMaths)
|
||||
{
|
||||
//mocking cb calls inside canAfford()
|
||||
|
||||
ON_CALL(gcm, getResourceAmount())
|
||||
.WillByDefault(Return(TResources(10, 0, 11, 0, 0, 0, 12345)));
|
||||
|
||||
TResources buildingCost(10, 0, 10, 0, 0, 0, 5000);
|
||||
//EXPECT_CALL(gcm, getResourceAmount()).RetiresOnSaturation();
|
||||
//EXPECT_CALL(gcm, getTownsInfo(_)).RetiresOnSaturation();
|
||||
EXPECT_NO_THROW(rm->canAfford(buildingCost));
|
||||
EXPECT_TRUE(rm->canAfford(buildingCost));
|
||||
|
||||
TResources armyCost(0, 0, 0, 0, 0, 0, 54321);
|
||||
EXPECT_FALSE(rm->canAfford(armyCost));
|
||||
|
||||
rm->reserveResoures(armyCost, gatherArmy);
|
||||
EXPECT_FALSE(rm->canAfford(buildingCost)) << "Reserved value should be substracted from free resources";
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, notifyGoalImplemented)
|
||||
{
|
||||
ASSERT_FALSE(rm->hasTasksLeft());
|
||||
|
||||
EXPECT_FALSE(rm->notifyGoalCompleted(invalidGoal));
|
||||
EXPECT_FALSE(rm->hasTasksLeft());
|
||||
|
||||
TResources res(0,0,0,0,0,0,12345);;
|
||||
rm->reserveResoures(res, invalidGoal);
|
||||
ASSERT_FALSE(rm->hasTasksLeft()) << "Can't push Invalid goal";
|
||||
EXPECT_FALSE(rm->notifyGoalCompleted(invalidGoal));
|
||||
|
||||
EXPECT_FALSE(rm->notifyGoalCompleted(gatherArmy)) << "Queue should be empty";
|
||||
rm->reserveResoures(res, gatherArmy);
|
||||
EXPECT_TRUE(rm->notifyGoalCompleted(gatherArmy)) << "Not implemented"; //TODO: try it with not a copy
|
||||
EXPECT_FALSE(rm->notifyGoalCompleted(gatherArmy)); //already completed
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, notifyFulfillsAll)
|
||||
{
|
||||
TResources res;
|
||||
ASSERT_TRUE(buildAny->fulfillsMe(buildThis)) << "Goal dependency implemented incorrectly"; //TODO: goal mock?
|
||||
rm->reserveResoures(res, buildAny);
|
||||
rm->reserveResoures(res, buildAny);
|
||||
rm->reserveResoures(res, buildAny);
|
||||
ASSERT_TRUE(rm->hasTasksLeft()); //regardless if duplicates are allowed or not
|
||||
rm->notifyGoalCompleted(buildThis);
|
||||
ASSERT_FALSE(rm->hasTasksLeft()) << "BuildThis didn't remove Build Any!";
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, queueOrder)
|
||||
{
|
||||
ASSERT_FALSE(rm->hasTasksLeft());
|
||||
|
||||
TSubgoal buildLow = sptr(StrictMock<BuildThis>()),
|
||||
buildBit = sptr(StrictMock<BuildThis>()),
|
||||
buildMed = sptr(StrictMock<BuildThis>()),
|
||||
buildHigh = sptr(StrictMock<BuildThis>()),
|
||||
buildVeryHigh = sptr(StrictMock<BuildThis>()),
|
||||
buildExtra = sptr(StrictMock<BuildThis>()),
|
||||
buildNotSoExtra = sptr(StrictMock<BuildThis>());
|
||||
|
||||
buildLow->setpriority(0).setbid(1);
|
||||
buildLow->setpriority(1).setbid(2);
|
||||
buildMed->setpriority(2).setbid(3);
|
||||
buildHigh->setpriority(5).setbid(4);
|
||||
buildVeryHigh->setpriority(10).setbid(5);
|
||||
|
||||
TResources price(0, 0, 0, 0, 0, 0, 1000);
|
||||
rm->reserveResoures(price, buildLow);
|
||||
rm->reserveResoures(price, buildHigh);
|
||||
rm->reserveResoures(price, buildMed);
|
||||
|
||||
ON_CALL(gcm, getResourceAmount())
|
||||
.WillByDefault(Return(TResources(0,0,0,0,0,0,4000,0))); //we can afford 4 top goals
|
||||
|
||||
auto goal = rm->whatToDo();
|
||||
EXPECT_EQ(goal->goalType, Goals::BUILD_STRUCTURE);
|
||||
ASSERT_EQ(rm->whatToDo()->bid, 4);
|
||||
rm->reserveResoures(price, buildBit);
|
||||
rm->reserveResoures(price, buildVeryHigh);
|
||||
goal = rm->whatToDo();
|
||||
EXPECT_EQ(goal->goalType, Goals::BUILD_STRUCTURE);
|
||||
ASSERT_EQ(goal->bid, 5);
|
||||
|
||||
buildExtra->setpriority(3).setbid(100);
|
||||
EXPECT_EQ(rm->whatToDo(price, buildExtra)->bid, 100);
|
||||
|
||||
buildNotSoExtra->setpriority(0.7f).setbid(7);
|
||||
goal = rm->whatToDo(price, buildNotSoExtra);
|
||||
EXPECT_NE(goal->bid, 7);
|
||||
EXPECT_EQ(goal->goalType, Goals::COLLECT_RES) << "We can't afford this goal, need to collect resources";
|
||||
EXPECT_EQ(goal->resID, Res::GOLD) << "We need to collect gold";
|
||||
|
||||
goal = rm->whatToDo();
|
||||
EXPECT_NE(goal->goalType, Goals::COLLECT_RES);
|
||||
EXPECT_EQ(goal->bid, 5) << "Should return highest-priority goal";
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, updateGoalImplemented)
|
||||
{
|
||||
ASSERT_FALSE(rm->hasTasksLeft());
|
||||
|
||||
TResources res;
|
||||
res[Res::GOLD] = 12345;
|
||||
|
||||
buildThis->setpriority(1);
|
||||
buildThis->bid = 666;
|
||||
|
||||
EXPECT_FALSE(rm->updateGoal(buildThis)); //try update with no objectives -> fail
|
||||
|
||||
rm->reserveResoures(res, buildThis);
|
||||
ASSERT_TRUE(rm->hasTasksLeft());
|
||||
buildThis->setpriority(4.444f);
|
||||
|
||||
auto buildThis2 = sptr(StrictMock<BuildThis>());
|
||||
buildThis2->bid = 777;
|
||||
buildThis2->setpriority(3.33f);
|
||||
|
||||
EXPECT_FALSE(rm->updateGoal(buildThis2)) << "Shouldn't update with wrong goal";
|
||||
EXPECT_TRUE(rm->updateGoal(buildThis)) << "Not implemented"; //try update with copy of reserved goal -> true
|
||||
|
||||
EXPECT_FALSE(rm->updateGoal(invalidGoal)) << "Can't update Invalid goal";
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, complexGoalUpdates)
|
||||
{
|
||||
//TODO
|
||||
ASSERT_FALSE(rm->hasTasksLeft());
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, tasksLeft)
|
||||
{
|
||||
ASSERT_FALSE(rm->hasTasksLeft());
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, reservedResources)
|
||||
{
|
||||
//TOOO, not worth it now
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, freeResources)
|
||||
{
|
||||
ON_CALL(gcm, getResourceAmount()) //in case callback or gs gets crazy
|
||||
.WillByDefault(Return(TResources(-1, 0, -13.0f, -38763, -93764, -464, -12, -98765)));
|
||||
|
||||
auto res = rm->freeResources();
|
||||
ASSERT_GE(res[Res::WOOD], 0);
|
||||
ASSERT_GE(res[Res::MERCURY], 0);
|
||||
ASSERT_GE(res[Res::ORE], 0);
|
||||
ASSERT_GE(res[Res::SULFUR], 0);
|
||||
ASSERT_GE(res[Res::CRYSTAL], 0);
|
||||
ASSERT_GE(res[Res::GEMS], 0);
|
||||
ASSERT_GE(res[Res::GOLD], 0);
|
||||
ASSERT_GE(res[Res::MITHRIL], 0);
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, freeResourcesWithManyGoals)
|
||||
{
|
||||
ON_CALL(gcm, getResourceAmount())
|
||||
.WillByDefault(Return(TResources(20, 10, 20, 10, 10, 10, 20000, 0)));
|
||||
|
||||
ASSERT_EQ(rm->freeResources(), TResources(20, 10, 20, 10, 10, 10, 20000, 0));
|
||||
|
||||
rm->reserveResoures(TResources(0, 4, 0, 0, 0, 0, 13000), gatherArmy);
|
||||
ASSERT_EQ(rm->freeResources(), TResources(20, 6, 20, 10, 10, 10, 7000, 0));
|
||||
rm->reserveResoures(TResources(5, 4, 5, 4, 4, 4, 5000), buildThis);
|
||||
ASSERT_EQ(rm->freeResources(), TResources(15, 2, 15, 6, 6, 6, 2000, 0));
|
||||
rm->reserveResoures(TResources(0, 0, 0, 0, 0, 0, 2500), recruitHero);
|
||||
auto res = rm->freeResources();
|
||||
EXPECT_EQ(res[Res::GOLD], 0) << "We should have 0 gold left";
|
||||
|
||||
ON_CALL(gcm, getResourceAmount())
|
||||
.WillByDefault(Return(TResources(20, 10, 20, 10, 10, 10, 30000, 0))); //+10000 gold
|
||||
|
||||
res = rm->freeResources();
|
||||
EXPECT_EQ(res[Res::GOLD], 9500) << "We should have extra savings now";
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, freeGold)
|
||||
{
|
||||
ON_CALL(gcm, getResourceAmount())
|
||||
.WillByDefault(Return(TResources(0, 0, 0, 0, 0, 0, 666)));
|
||||
|
||||
ASSERT_EQ(rm->freeGold(), 666);
|
||||
|
||||
ON_CALL(gcm, getResourceAmount())
|
||||
.WillByDefault(Return(TResources(0, 0, 0, 0, 0, 0, -3762363)));
|
||||
|
||||
ASSERT_GE(rm->freeGold(), 0) << "We should never see negative savings";
|
||||
}
|
18
test/vcai/mock_ResourceManager.cpp
Normal file
18
test/vcai/mock_ResourceManager.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "mock_ResourceManager.h"
|
||||
|
||||
void ResourceManagerMock::reserveResoures(TResources &res, Goals::TSubgoal goal)
|
||||
{
|
||||
ResourceManager::reserveResoures(res, goal);
|
||||
}
|
||||
|
||||
bool ResourceManagerMock::updateGoal(Goals::TSubgoal goal)
|
||||
{
|
||||
return ResourceManager::updateGoal(goal);
|
||||
}
|
||||
|
||||
bool ResourceManagerMock::notifyGoalCompleted(Goals::TSubgoal goal)
|
||||
{
|
||||
return ResourceManager::notifyGoalCompleted(goal);
|
||||
}
|
19
test/vcai/mock_ResourceManager.h
Normal file
19
test/vcai/mock_ResourceManager.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "../AI/VCAI/ResourceManager.h"
|
||||
|
||||
class ResourceManager;
|
||||
class CPlayerSpecificInfoCallback;
|
||||
class VCAI;
|
||||
|
||||
class ResourceManagerMock : public ResourceManager
|
||||
{
|
||||
friend class ResourceManagerTest; //everything is public
|
||||
public:
|
||||
using ResourceManager::ResourceManager;
|
||||
//access protected members, TODO: consider other architecture?
|
||||
void reserveResoures(TResources &res, Goals::TSubgoal goal = Goals::TSubgoal()) override;
|
||||
bool updateGoal(Goals::TSubgoal goal) override;
|
||||
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
|
||||
};
|
23
test/vcai/mock_VCAI.cpp
Normal file
23
test/vcai/mock_VCAI.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* ResourceManagerTest.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 "mock_VCAI.h"
|
||||
|
||||
VCAIMock::VCAIMock()
|
||||
: VCAI()
|
||||
{
|
||||
makingTurn = nullptr;
|
||||
}
|
||||
|
||||
VCAIMock::~VCAIMock()
|
||||
{
|
||||
}
|
96
test/vcai/mock_VCAI.h
Normal file
96
test/vcai/mock_VCAI.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* mock_VCAI.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 "gtest/gtest.h"
|
||||
|
||||
#include "../AI/VCAI/VCAI.h"
|
||||
|
||||
//dependency hell
|
||||
#include "../../lib/NetPacks.h" //for Component, TryMoveHero
|
||||
#include "../../lib/serializer/BinarySerializer.h"
|
||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||
|
||||
class VCAIMock : public VCAI
|
||||
{
|
||||
public:
|
||||
VCAIMock();
|
||||
~VCAIMock() override;
|
||||
|
||||
//overloading all "override" methods from VCAI. AI should never call them, anyway
|
||||
MOCK_CONST_METHOD0(getBattleAIName, std::string());
|
||||
|
||||
MOCK_METHOD1(init, void(std::shared_ptr<CCallback> CB));
|
||||
MOCK_METHOD0(yourTurn, void());
|
||||
|
||||
MOCK_METHOD4(heroGotLevel, void(const CGHeroInstance * hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID));
|
||||
MOCK_METHOD3(commanderGotLevel, void(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID));
|
||||
MOCK_METHOD6(showBlockingDialog, void(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel));
|
||||
MOCK_METHOD4(showGarrisonDialog, void(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID));
|
||||
MOCK_METHOD4(showTeleportDialog, void(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID));
|
||||
MOCK_METHOD5(showMapObjectSelectDialog, void(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects));
|
||||
MOCK_METHOD2(saveGame, void(BinarySerializer & h, const int version));
|
||||
MOCK_METHOD2(loadGame, void(BinaryDeserializer & h, const int version));
|
||||
MOCK_METHOD0(finish, void());
|
||||
|
||||
MOCK_METHOD1(availableCreaturesChanged, void(const CGDwelling * town));
|
||||
MOCK_METHOD1(heroMoved, void(const TryMoveHero & details));
|
||||
MOCK_METHOD1(heroInGarrisonChange, void(const CGTownInstance * town));
|
||||
MOCK_METHOD2(centerView, void(int3 pos, int focusTime));
|
||||
MOCK_METHOD1(tileHidden, void(const std::unordered_set<int3, ShashInt3> & pos));
|
||||
MOCK_METHOD2(artifactMoved, void(const ArtifactLocation & src, const ArtifactLocation & dst));
|
||||
MOCK_METHOD1(artifactAssembled, void(const ArtifactLocation & al));
|
||||
MOCK_METHOD1(showTavernWindow, void(const CGObjectInstance * townOrTavern));
|
||||
MOCK_METHOD1(showThievesGuildWindow, void(const CGObjectInstance * obj));
|
||||
MOCK_METHOD2(playerBlocked, void(int reason, bool start));
|
||||
MOCK_METHOD0(showPuzzleMap, void());
|
||||
MOCK_METHOD1(showShipyardDialog, void(const IShipyard * obj));
|
||||
MOCK_METHOD2(gameOver, void(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult));
|
||||
MOCK_METHOD1(artifactPut, void(const ArtifactLocation & al));
|
||||
MOCK_METHOD1(artifactRemoved, void(const ArtifactLocation & al));
|
||||
MOCK_METHOD1(artifactDisassembled, void(const ArtifactLocation & al));
|
||||
MOCK_METHOD3(heroVisit, void(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start));
|
||||
MOCK_METHOD1(availableArtifactsChanged, void(const CGBlackMarket * bm));
|
||||
MOCK_METHOD2(heroVisitsTown, void(const CGHeroInstance * hero, const CGTownInstance * town));
|
||||
MOCK_METHOD1(tileRevealed, void(const std::unordered_set<int3, ShashInt3> & pos));
|
||||
MOCK_METHOD3(heroExchangeStarted, void(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query));
|
||||
MOCK_METHOD3(heroPrimarySkillChanged, void(const CGHeroInstance * hero, int which, si64 val));
|
||||
MOCK_METHOD3(showRecruitmentDialog, void(const CGDwelling * dwelling, const CArmedInstance * dst, int level));
|
||||
MOCK_METHOD1(heroMovePointsChanged, void(const CGHeroInstance * hero));
|
||||
MOCK_METHOD2(garrisonsChanged, void(ObjectInstanceID id1, ObjectInstanceID id2));
|
||||
MOCK_METHOD1(newObject, void(const CGObjectInstance * obj));
|
||||
MOCK_METHOD2(showHillFortWindow, void(const CGObjectInstance * object, const CGHeroInstance * visitor));
|
||||
MOCK_METHOD2(playerBonusChanged, void(const Bonus & bonus, bool gain));
|
||||
MOCK_METHOD1(heroCreated, void(const CGHeroInstance *));
|
||||
MOCK_METHOD2(advmapSpellCast, void(const CGHeroInstance * caster, int spellID));
|
||||
MOCK_METHOD3(showInfoDialog, void(const std::string & text, const std::vector<Component> & components, int soundID));
|
||||
MOCK_METHOD1(requestRealized, void(PackageApplied * pa));
|
||||
MOCK_METHOD0(receivedResource, void());
|
||||
MOCK_METHOD1(objectRemoved, void(const CGObjectInstance * obj));
|
||||
MOCK_METHOD2(showUniversityWindow, void(const IMarket * market, const CGHeroInstance * visitor));
|
||||
MOCK_METHOD1(heroManaPointsChanged, void(const CGHeroInstance * hero));
|
||||
MOCK_METHOD3(heroSecondarySkillChanged, void(const CGHeroInstance * hero, int which, int val));
|
||||
MOCK_METHOD0(battleResultsApplied, void());
|
||||
MOCK_METHOD1(objectPropertyChanged, void(const SetObjectProperty * sop));
|
||||
MOCK_METHOD3(buildChanged, void(const CGTownInstance * town, BuildingID buildingID, int what));
|
||||
MOCK_METHOD3(heroBonusChanged, void(const CGHeroInstance * hero, const Bonus & bonus, bool gain));
|
||||
MOCK_METHOD2(showMarketWindow, void(const IMarket * market, const CGHeroInstance * visitor));
|
||||
MOCK_METHOD1(showWorldViewEx, void(const std::vector<ObjectPosInfo> & objectPositions));
|
||||
|
||||
MOCK_METHOD6(battleStart, void(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side));
|
||||
MOCK_METHOD1(battleEnd, void(const BattleResult * br));
|
||||
|
||||
MOCK_METHOD2(requestSent, void(const CPackForServer * pack, int requestID));
|
||||
|
||||
//interetsing stuff
|
||||
|
||||
MOCK_CONST_METHOD0(getFlaggedObjects, std::vector<const CGObjectInstance *>());
|
||||
|
||||
};
|
35
test/vcai/mock_VCAI_CGoal.h
Normal file
35
test/vcai/mock_VCAI_CGoal.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* mock_VCAI_CGoal.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 "../AI/VCAI/Goals.h"
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class InvalidGoalMock : public Invalid
|
||||
{
|
||||
public:
|
||||
//MOCK_METHOD1(accept, void(VCAI *));
|
||||
//MOCK_METHOD1(accept, float(FuzzyHelper *));
|
||||
//MOCK_METHOD1(fulfillsMe, bool(TSubgoal));
|
||||
//bool operator==(AbstractGoal & g) override
|
||||
//{
|
||||
// return false;
|
||||
//};
|
||||
//MOCK_METHOD0(getAllPossibleSubgoals, TGoalVec());
|
||||
//MOCK_METHOD0(whatToDoToAchieve, TSubgoal());
|
||||
|
||||
};
|
||||
|
||||
class GatherArmyGoalMock : public GatherArmy
|
||||
{
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user