mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Nullkiller: copy VCAI
This commit is contained in:
parent
e407d4e547
commit
be4f803d4a
388
AI/Nullkiller/AIUtility.cpp
Normal file
388
AI/Nullkiller/AIUtility.cpp
Normal file
@ -0,0 +1,388 @@
|
||||
/*
|
||||
* AIUtility.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 "AIUtility.h"
|
||||
#include "VCAI.h"
|
||||
#include "FuzzyHelper.h"
|
||||
#include "Goals/Goals.h"
|
||||
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/mapObjects/CBank.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapObjects/CQuest.h"
|
||||
#include "../../lib/mapping/CMapDefines.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
//extern static const int3 dirs[8];
|
||||
|
||||
const CGObjectInstance * ObjectIdRef::operator->() const
|
||||
{
|
||||
return cb->getObj(id, false);
|
||||
}
|
||||
|
||||
ObjectIdRef::operator const CGObjectInstance *() const
|
||||
{
|
||||
return cb->getObj(id, false);
|
||||
}
|
||||
|
||||
ObjectIdRef::operator bool() const
|
||||
{
|
||||
return cb->getObj(id, false);
|
||||
}
|
||||
|
||||
ObjectIdRef::ObjectIdRef(ObjectInstanceID _id)
|
||||
: id(_id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj)
|
||||
: id(obj->id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ObjectIdRef::operator<(const ObjectIdRef & rhs) const
|
||||
{
|
||||
return id < rhs.id;
|
||||
}
|
||||
|
||||
HeroPtr::HeroPtr(const CGHeroInstance * H)
|
||||
{
|
||||
if(!H)
|
||||
{
|
||||
//init from nullptr should equal to default init
|
||||
*this = HeroPtr();
|
||||
return;
|
||||
}
|
||||
|
||||
h = H;
|
||||
name = h->name;
|
||||
hid = H->id;
|
||||
// infosCount[ai->playerID][hid]++;
|
||||
}
|
||||
|
||||
HeroPtr::HeroPtr()
|
||||
{
|
||||
h = nullptr;
|
||||
hid = ObjectInstanceID();
|
||||
}
|
||||
|
||||
HeroPtr::~HeroPtr()
|
||||
{
|
||||
// if(hid >= 0)
|
||||
// infosCount[ai->playerID][hid]--;
|
||||
}
|
||||
|
||||
bool HeroPtr::operator<(const HeroPtr & rhs) const
|
||||
{
|
||||
return hid < rhs.hid;
|
||||
}
|
||||
|
||||
const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
|
||||
{
|
||||
//TODO? check if these all assertions every time we get info about hero affect efficiency
|
||||
//
|
||||
//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
|
||||
assert(doWeExpectNull || h);
|
||||
|
||||
if(h)
|
||||
{
|
||||
auto obj = cb->getObj(hid);
|
||||
const bool owned = obj && obj->tempOwner == ai->playerID;
|
||||
|
||||
if(doWeExpectNull && !owned)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(obj);
|
||||
assert(owned);
|
||||
}
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
const CGHeroInstance * HeroPtr::operator->() const
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
bool HeroPtr::validAndSet() const
|
||||
{
|
||||
return get(true);
|
||||
}
|
||||
|
||||
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
|
||||
// callback pointer is thread-specific and slow to retrieve -> read map size only once
|
||||
int3 mapSize = cb->getMapSize();
|
||||
for(int i = 0; i < mapSize.x; i++)
|
||||
{
|
||||
for(int j = 0; j < mapSize.y; j++)
|
||||
{
|
||||
for(int k = 0; k < mapSize.z; k++)
|
||||
foo(int3(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 & pos)> foo)
|
||||
{
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
for(int i = 0; i < mapSize.x; i++)
|
||||
{
|
||||
for(int j = 0; j < mapSize.y; j++)
|
||||
{
|
||||
for(int k = 0; k < mapSize.z; k++)
|
||||
foo(cbp, int3(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_neighbour(const int3 & pos, std::function<void(const int3 & pos)> foo)
|
||||
{
|
||||
CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
|
||||
for(const int3 & dir : int3::getDirs())
|
||||
{
|
||||
const int3 n = pos + dir;
|
||||
if(cbp->isInTheMap(n))
|
||||
foo(pos + dir);
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCallback * cbp, const int3 & pos)> foo)
|
||||
{
|
||||
for(const int3 & dir : int3::getDirs())
|
||||
{
|
||||
const int3 n = pos + dir;
|
||||
if(cbp->isInTheMap(n))
|
||||
foo(cbp, pos + dir);
|
||||
}
|
||||
}
|
||||
|
||||
bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const
|
||||
{
|
||||
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
|
||||
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
|
||||
|
||||
return ln->cost < rn->cost;
|
||||
}
|
||||
|
||||
bool isSafeToVisit(HeroPtr h, crint3 tile)
|
||||
{
|
||||
return isSafeToVisit(h, fh->evaluateDanger(tile, h.get()));
|
||||
}
|
||||
|
||||
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
|
||||
{
|
||||
const ui64 heroStrength = h->getTotalStrength();
|
||||
|
||||
if(dangerStrength)
|
||||
{
|
||||
if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true; //there's no danger
|
||||
}
|
||||
|
||||
bool isObjectRemovable(const CGObjectInstance * obj)
|
||||
{
|
||||
//FIXME: move logic to object property!
|
||||
switch (obj->ID)
|
||||
{
|
||||
case Obj::MONSTER:
|
||||
case Obj::RESOURCE:
|
||||
case Obj::CAMPFIRE:
|
||||
case Obj::TREASURE_CHEST:
|
||||
case Obj::ARTIFACT:
|
||||
case Obj::BORDERGUARD:
|
||||
case Obj::FLOTSAM:
|
||||
case Obj::PANDORAS_BOX:
|
||||
case Obj::OCEAN_BOTTLE:
|
||||
case Obj::SEA_CHEST:
|
||||
case Obj::SHIPWRECK_SURVIVOR:
|
||||
case Obj::SPELL_SCROLL:
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
|
||||
{
|
||||
// TODO: Such information should be provided by pathfinder
|
||||
// Tile must be free or with unoccupied boat
|
||||
if(!t->blocked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(!fromWater) // do not try to board when in water sector
|
||||
{
|
||||
if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
|
||||
{
|
||||
if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
|
||||
return false;
|
||||
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
|
||||
return !gate->passableFor(ai->playerID);
|
||||
}
|
||||
|
||||
bool isBlockVisitObj(const int3 & pos)
|
||||
{
|
||||
if(auto obj = cb->getTopObj(pos))
|
||||
{
|
||||
if(obj->blockVisit) //we can't stand on that object
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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(const CArmedInstance * h, const CGDwelling * t)
|
||||
{
|
||||
ui64 aivalue = 0;
|
||||
TResources availableRes = cb->getResourceAmount();
|
||||
int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
|
||||
|
||||
for(auto const dc : t->creatures)
|
||||
{
|
||||
creInfo ci = infoFromDC(dc);
|
||||
|
||||
if(!ci.count || ci.creID == -1)
|
||||
continue;
|
||||
|
||||
vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford
|
||||
|
||||
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;
|
||||
availableRes -= ci.cre->cost * ci.count;
|
||||
}
|
||||
}
|
||||
|
||||
return aivalue;
|
||||
}
|
||||
|
||||
ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t)
|
||||
{
|
||||
ui64 ret = 0;
|
||||
int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
|
||||
std::vector<const CStackInstance *> toMove;
|
||||
for(auto const slot : t->Slots())
|
||||
{
|
||||
//can be merged woth another stack?
|
||||
SlotID dst = h->getSlotFor(slot.second->getCreatureID());
|
||||
if(h->hasStackAtSlot(dst))
|
||||
ret += t->getPower(slot.first);
|
||||
else
|
||||
toMove.push_back(slot.second);
|
||||
}
|
||||
boost::sort(toMove, [](const CStackInstance * lhs, const CStackInstance * rhs)
|
||||
{
|
||||
return lhs->getPower() < rhs->getPower();
|
||||
});
|
||||
for(auto & stack : boost::adaptors::reverse(toMove))
|
||||
{
|
||||
if(freeHeroSlots)
|
||||
{
|
||||
ret += stack->getPower();
|
||||
freeHeroSlots--;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool compareHeroStrength(HeroPtr h1, HeroPtr h2)
|
||||
{
|
||||
return h1->getTotalStrength() < h2->getTotalStrength();
|
||||
}
|
||||
|
||||
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
|
||||
{
|
||||
return a1->getArmyStrength() < a2->getArmyStrength();
|
||||
}
|
||||
|
||||
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2)
|
||||
{
|
||||
auto art1 = a1->artType;
|
||||
auto art2 = a2->artType;
|
||||
|
||||
if(art1->price == art2->price)
|
||||
return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
|
||||
else if(art1->price > art2->price)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
189
AI/Nullkiller/AIUtility.h
Normal file
189
AI/Nullkiller/AIUtility.h
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* AIUtility.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 "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CStopWatch.h"
|
||||
#include "../../lib/mapObjects/CObjectHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#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;
|
||||
const int ALLOWED_ROAMING_HEROES = 8;
|
||||
|
||||
//implementation-dependent
|
||||
extern const double SAFE_ATTACK_CONSTANT;
|
||||
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 DLL_EXPORT HeroPtr
|
||||
{
|
||||
const CGHeroInstance * h;
|
||||
ObjectInstanceID hid;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
|
||||
HeroPtr();
|
||||
HeroPtr(const CGHeroInstance * H);
|
||||
~HeroPtr();
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return validAndSet();
|
||||
}
|
||||
|
||||
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;
|
||||
bool operator!=(const HeroPtr & rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
const CGHeroInstance * get(bool doWeExpectNull = false) const;
|
||||
bool validAndSet() const;
|
||||
|
||||
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & this->h;
|
||||
h & hid;
|
||||
h & name;
|
||||
}
|
||||
};
|
||||
|
||||
enum BattleState
|
||||
{
|
||||
NO_BATTLE,
|
||||
UPCOMING_BATTLE,
|
||||
ONGOING_BATTLE,
|
||||
ENDING_BATTLE
|
||||
};
|
||||
|
||||
// AI lives in a dangerous world. CGObjectInstances under pointer may got deleted/hidden.
|
||||
// This class stores object id, so we can detect when we lose access to the underlying object.
|
||||
struct ObjectIdRef
|
||||
{
|
||||
ObjectInstanceID id;
|
||||
|
||||
const CGObjectInstance * operator->() const;
|
||||
operator const CGObjectInstance *() const;
|
||||
operator bool() const;
|
||||
|
||||
ObjectIdRef(ObjectInstanceID _id);
|
||||
ObjectIdRef(const CGObjectInstance * obj);
|
||||
|
||||
bool operator<(const ObjectIdRef & rhs) const;
|
||||
|
||||
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & id;
|
||||
}
|
||||
};
|
||||
|
||||
struct TimeCheck
|
||||
{
|
||||
CStopWatch time;
|
||||
std::string txt;
|
||||
TimeCheck(crstring TXT)
|
||||
: txt(TXT)
|
||||
{
|
||||
}
|
||||
|
||||
~TimeCheck()
|
||||
{
|
||||
logAi->trace("Time of %s was %d ms.", txt, time.getDiff());
|
||||
}
|
||||
};
|
||||
|
||||
//TODO: replace with vstd::
|
||||
struct AtScopeExit
|
||||
{
|
||||
std::function<void()> foo;
|
||||
AtScopeExit(const std::function<void()> & FOO)
|
||||
: foo(FOO)
|
||||
{}
|
||||
~AtScopeExit()
|
||||
{
|
||||
foo();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ObjsVector : public std::vector<ObjectIdRef>
|
||||
{
|
||||
};
|
||||
|
||||
template<int id>
|
||||
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);
|
||||
void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCallback * cbp, const int3 & pos)> foo); // avoid costly retrieval of thread-specific pointer
|
||||
|
||||
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
|
||||
bool isBlockedBorderGate(int3 tileToHit);
|
||||
bool isBlockVisitObj(const int3 & pos);
|
||||
|
||||
bool isWeeklyRevisitable(const CGObjectInstance * obj);
|
||||
bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
|
||||
|
||||
bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
|
||||
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
|
||||
bool isSafeToVisit(HeroPtr h, crint3 tile);
|
||||
|
||||
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
|
||||
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
||||
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
||||
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t);
|
||||
ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t);
|
||||
|
||||
class CDistanceSorter
|
||||
{
|
||||
const CGHeroInstance * hero;
|
||||
|
||||
public:
|
||||
CDistanceSorter(const CGHeroInstance * hero)
|
||||
: hero(hero)
|
||||
{
|
||||
}
|
||||
bool operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const;
|
||||
};
|
159
AI/Nullkiller/AIhelper.cpp
Normal file
159
AI/Nullkiller/AIhelper.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "BuildingManager.h"
|
||||
|
||||
AIhelper::AIhelper()
|
||||
{
|
||||
resourceManager.reset(new ResourceManager());
|
||||
buildingManager.reset(new BuildingManager());
|
||||
pathfindingManager.reset(new PathfindingManager());
|
||||
}
|
||||
|
||||
AIhelper::~AIhelper()
|
||||
{
|
||||
}
|
||||
|
||||
bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal)
|
||||
{
|
||||
return resourceManager->notifyGoalCompleted(goal);
|
||||
}
|
||||
|
||||
void AIhelper::init(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
resourceManager->init(CB);
|
||||
buildingManager->init(CB);
|
||||
pathfindingManager->init(CB);
|
||||
}
|
||||
|
||||
void AIhelper::setAI(VCAI * AI)
|
||||
{
|
||||
resourceManager->setAI(AI);
|
||||
buildingManager->setAI(AI);
|
||||
pathfindingManager->setAI(AI);
|
||||
}
|
||||
|
||||
bool AIhelper::getBuildingOptions(const CGTownInstance * t)
|
||||
{
|
||||
return buildingManager->getBuildingOptions(t);
|
||||
}
|
||||
|
||||
BuildingID AIhelper::getMaxPossibleGoldBuilding(const CGTownInstance * t)
|
||||
{
|
||||
return buildingManager->getMaxPossibleGoldBuilding(t);
|
||||
}
|
||||
|
||||
boost::optional<PotentialBuilding> AIhelper::immediateBuilding() const
|
||||
{
|
||||
return buildingManager->immediateBuilding();
|
||||
}
|
||||
|
||||
boost::optional<PotentialBuilding> AIhelper::expensiveBuilding() const
|
||||
{
|
||||
return buildingManager->expensiveBuilding();
|
||||
}
|
||||
|
||||
boost::optional<BuildingID> AIhelper::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
|
||||
{
|
||||
return buildingManager->canBuildAnyStructure(t, buildList, maxDays);
|
||||
}
|
||||
|
||||
Goals::TSubgoal AIhelper::whatToDo(TResources & res, Goals::TSubgoal goal)
|
||||
{
|
||||
return resourceManager->whatToDo(res, goal);
|
||||
}
|
||||
|
||||
Goals::TSubgoal AIhelper::whatToDo() const
|
||||
{
|
||||
return resourceManager->whatToDo();
|
||||
}
|
||||
|
||||
bool AIhelper::containsObjective(Goals::TSubgoal goal) const
|
||||
{
|
||||
return resourceManager->containsObjective(goal);
|
||||
}
|
||||
|
||||
bool AIhelper::hasTasksLeft() const
|
||||
{
|
||||
return resourceManager->hasTasksLeft();
|
||||
}
|
||||
|
||||
bool AIhelper::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal&)> predicate)
|
||||
{
|
||||
return resourceManager->removeOutdatedObjectives(predicate);
|
||||
}
|
||||
|
||||
bool AIhelper::canAfford(const TResources & cost) const
|
||||
{
|
||||
return resourceManager->canAfford(cost);
|
||||
}
|
||||
|
||||
TResources AIhelper::reservedResources() const
|
||||
{
|
||||
return resourceManager->reservedResources();
|
||||
}
|
||||
|
||||
TResources AIhelper::freeResources() const
|
||||
{
|
||||
return resourceManager->freeResources();
|
||||
}
|
||||
|
||||
TResource AIhelper::freeGold() const
|
||||
{
|
||||
return resourceManager->freeGold();
|
||||
}
|
||||
|
||||
TResources AIhelper::allResources() const
|
||||
{
|
||||
return resourceManager->allResources();
|
||||
}
|
||||
|
||||
TResource AIhelper::allGold() const
|
||||
{
|
||||
return resourceManager->allGold();
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitTile(const int3 & tile) const
|
||||
{
|
||||
return pathfindingManager->howToVisitTile(tile);
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitObj(ObjectIdRef obj) const
|
||||
{
|
||||
return pathfindingManager->howToVisitObj(obj);
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
|
||||
{
|
||||
return pathfindingManager->howToVisitTile(hero, tile, allowGatherArmy);
|
||||
}
|
||||
|
||||
Goals::TGoalVec AIhelper::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
|
||||
{
|
||||
return pathfindingManager->howToVisitObj(hero, obj, allowGatherArmy);
|
||||
}
|
||||
|
||||
std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
return pathfindingManager->getPathsToTile(hero, tile);
|
||||
}
|
||||
|
||||
void AIhelper::updatePaths(std::vector<HeroPtr> heroes)
|
||||
{
|
||||
pathfindingManager->updatePaths(heroes);
|
||||
}
|
||||
|
||||
void AIhelper::updatePaths(const HeroPtr & hero)
|
||||
{
|
||||
pathfindingManager->updatePaths(hero);
|
||||
}
|
78
AI/Nullkiller/AIhelper.h
Normal file
78
AI/Nullkiller/AIhelper.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "BuildingManager.h"
|
||||
#include "Pathfinding/PathfindingManager.h"
|
||||
|
||||
class ResourceManager;
|
||||
class BuildingManager;
|
||||
|
||||
|
||||
//indirection interface for various modules
|
||||
class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager
|
||||
{
|
||||
friend class VCAI;
|
||||
friend struct SetGlobalState; //mess?
|
||||
|
||||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<BuildingManager> buildingManager;
|
||||
std::shared_ptr<PathfindingManager> pathfindingManager;
|
||||
//TODO: vector<IAbstractManager>
|
||||
public:
|
||||
AIhelper();
|
||||
~AIhelper();
|
||||
|
||||
bool canAfford(const TResources & cost) const;
|
||||
TResources reservedResources() const override;
|
||||
TResources freeResources() const override;
|
||||
TResource freeGold() const override;
|
||||
TResources allResources() const override;
|
||||
TResource allGold() const override;
|
||||
|
||||
Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) override;
|
||||
Goals::TSubgoal whatToDo() const override;
|
||||
bool containsObjective(Goals::TSubgoal goal) const override;
|
||||
bool hasTasksLeft() const override;
|
||||
bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
|
||||
|
||||
bool getBuildingOptions(const CGTownInstance * t) override;
|
||||
BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
|
||||
boost::optional<PotentialBuilding> immediateBuilding() const override;
|
||||
boost::optional<PotentialBuilding> expensiveBuilding() const override;
|
||||
boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
|
||||
|
||||
Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitTile(const int3 & tile) const override;
|
||||
Goals::TGoalVec howToVisitObj(ObjectIdRef obj) const override;
|
||||
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes) override;
|
||||
void updatePaths(const HeroPtr & hero) override;
|
||||
|
||||
STRONG_INLINE
|
||||
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
return pathfindingManager->isTileAccessible(hero, tile);
|
||||
}
|
||||
|
||||
private:
|
||||
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
|
||||
|
||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
};
|
||||
|
260
AI/Nullkiller/BuildingManager.cpp
Normal file
260
AI/Nullkiller/BuildingManager.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* BuildingManager.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "BuildingManager.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays)
|
||||
{
|
||||
if (maxDays == 0)
|
||||
{
|
||||
logAi->warn("Request to build building %d in 0 days!", building.toEnum());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vstd::contains(t->town->buildings, building))
|
||||
return false; // no such building in town
|
||||
|
||||
if (t->hasBuilt(building)) //Already built? Shouldn't happen in general
|
||||
return true;
|
||||
|
||||
const CBuilding * buildPtr = t->town->buildings.at(building);
|
||||
|
||||
auto toBuild = buildPtr->requirements.getFulfillmentCandidates([&](const BuildingID & buildID)
|
||||
{
|
||||
return t->hasBuilt(buildID);
|
||||
});
|
||||
toBuild.push_back(building);
|
||||
|
||||
for (BuildingID buildID : toBuild)
|
||||
{
|
||||
EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
|
||||
if (canBuild == EBuildingState::HAVE_CAPITAL || canBuild == EBuildingState::FORBIDDEN || canBuild == EBuildingState::NO_WATER)
|
||||
return false; //we won't be able to build this
|
||||
}
|
||||
|
||||
if (maxDays && toBuild.size() > maxDays)
|
||||
return false;
|
||||
|
||||
//TODO: calculate if we have enough resources to build it in maxDays?
|
||||
|
||||
for (const auto & buildID : toBuild)
|
||||
{
|
||||
const CBuilding * b = t->town->buildings.at(buildID);
|
||||
|
||||
EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
|
||||
if (canBuild == EBuildingState::ALLOWED)
|
||||
{
|
||||
PotentialBuilding pb;
|
||||
pb.bid = buildID;
|
||||
pb.price = t->getBuildingCost(buildID);
|
||||
immediateBuildings.push_back(pb); //these are checked again in try
|
||||
return true;
|
||||
}
|
||||
else if (canBuild == EBuildingState::PREREQUIRES)
|
||||
{
|
||||
// can happen when dependencies have their own missing dependencies
|
||||
if (tryBuildThisStructure(t, buildID, maxDays - 1))
|
||||
return true;
|
||||
}
|
||||
else if (canBuild == EBuildingState::MISSING_BASE)
|
||||
{
|
||||
if (tryBuildThisStructure(t, b->upgrade, maxDays - 1))
|
||||
return true;
|
||||
}
|
||||
else if (canBuild == EBuildingState::NO_RESOURCES)
|
||||
{
|
||||
//we may need to gather resources for those
|
||||
PotentialBuilding pb;
|
||||
pb.bid = buildID;
|
||||
pb.price = t->getBuildingCost(buildID);
|
||||
expensiveBuildings.push_back(pb); //these are checked again in try
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BuildingManager::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
|
||||
{
|
||||
for (const auto & building : buildList)
|
||||
{
|
||||
if (t->hasBuilt(building))
|
||||
continue;
|
||||
return tryBuildThisStructure(t, building, maxDays);
|
||||
|
||||
}
|
||||
return false; //Can't build anything
|
||||
}
|
||||
|
||||
boost::optional<BuildingID> BuildingManager::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
|
||||
{
|
||||
for (const auto & building : buildList)
|
||||
{
|
||||
if (t->hasBuilt(building))
|
||||
continue;
|
||||
switch (cb->canBuildStructure(t, building))
|
||||
{
|
||||
case EBuildingState::ALLOWED:
|
||||
case EBuildingState::NO_RESOURCES: //TODO: allow this via optional parameter?
|
||||
return boost::optional<BuildingID>(building);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return boost::optional<BuildingID>(); //Can't build anything
|
||||
}
|
||||
|
||||
bool BuildingManager::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
|
||||
{
|
||||
for (const auto & building : buildList)
|
||||
{
|
||||
if (t->hasBuilt(building))
|
||||
continue;
|
||||
return tryBuildThisStructure(t, building, maxDays);
|
||||
}
|
||||
return false; //Nothing to build
|
||||
}
|
||||
|
||||
void BuildingManager::init(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
cb = CB;
|
||||
}
|
||||
|
||||
void BuildingManager::setAI(VCAI * AI)
|
||||
{
|
||||
ai = AI;
|
||||
}
|
||||
//Set of buildings for different goals. Does not include any prerequisites.
|
||||
static const std::vector<BuildingID> essential = { BuildingID::TAVERN, BuildingID::TOWN_HALL };
|
||||
static const std::vector<BuildingID> basicGoldSource = { BuildingID::TOWN_HALL, BuildingID::CITY_HALL };
|
||||
static const std::vector<BuildingID> capitolAndRequirements = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::CAPITOL };
|
||||
static const std::vector<BuildingID> unitsSource = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
|
||||
BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 };
|
||||
static const std::vector<BuildingID> unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
|
||||
BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP };
|
||||
static const std::vector<BuildingID> unitGrowth = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
|
||||
BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR };
|
||||
static const std::vector<BuildingID> _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
|
||||
BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 };
|
||||
static const std::vector<BuildingID> extra = { BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
|
||||
BuildingID::SPECIAL_4, BuildingID::SHIPYARD }; // all remaining buildings
|
||||
|
||||
bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
|
||||
{
|
||||
//TODO make *real* town development system
|
||||
//TODO: faction-specific development: use special buildings, build dwellings in better order, etc
|
||||
//TODO: build resource silo, defences when needed
|
||||
//Possible - allow "locking" on specific building (build prerequisites and then building itself)
|
||||
|
||||
//TODO: There is some disabled building code in GatherTroops and GatherArmy - take it into account when enhancing building. For now AI works best with building only via Build goal.
|
||||
|
||||
immediateBuildings.clear();
|
||||
expensiveBuildings.clear();
|
||||
|
||||
//below algorithm focuses on economy growth at start of the game, saving money instead of build rushing is handled by Build goal
|
||||
//changing code blocks order will alter behavior by changing order of adding elements to immediateBuildings / expensiveBuildings
|
||||
|
||||
TResources currentRes = cb->getResourceAmount();
|
||||
TResources currentIncome = t->dailyIncome();
|
||||
|
||||
if(tryBuildAnyStructure(t, essential))
|
||||
return true;
|
||||
|
||||
//the more gold the better and less problems later //TODO: what about building mage guild / marketplace etc. with city hall disabled in editor?
|
||||
if(tryBuildNextStructure(t, basicGoldSource))
|
||||
return true;
|
||||
|
||||
//workaround for mantis #2696 - build capitol with separate algorithm if it is available
|
||||
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
|
||||
{
|
||||
if(tryBuildNextStructure(t, capitolAndRequirements))
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!t->hasBuilt(BuildingID::FORT)) //in vast majority of situations fort is top priority building if we already have city hall, TODO: unite with unitGrowth building chain
|
||||
if(tryBuildThisStructure(t, BuildingID::FORT))
|
||||
return true;
|
||||
|
||||
|
||||
|
||||
if (cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth
|
||||
{
|
||||
if (tryBuildNextStructure(t, unitGrowth, 2))
|
||||
return true;
|
||||
}
|
||||
|
||||
//try building dwellings
|
||||
if (t->hasBuilt(BuildingID::FORT))
|
||||
{
|
||||
if (tryBuildAnyStructure(t, unitsSource, 8 - cb->getDate(Date::DAY_OF_WEEK)))
|
||||
return true;
|
||||
}
|
||||
|
||||
//try to upgrade dwelling
|
||||
for (int i = 0; i < unitsUpgrade.size(); i++)
|
||||
{
|
||||
if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]) && t->hasBuilt(BuildingID::FORT))
|
||||
{
|
||||
if (tryBuildThisStructure(t, unitsUpgrade[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//remaining tasks
|
||||
if (tryBuildNextStructure(t, _spells))
|
||||
return true;
|
||||
if (tryBuildAnyStructure(t, extra))
|
||||
return true;
|
||||
|
||||
//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
|
||||
std::vector<BuildingID> extraBuildings;
|
||||
for (auto buildingInfo : t->town->buildings)
|
||||
{
|
||||
if (buildingInfo.first > 43)
|
||||
extraBuildings.push_back(buildingInfo.first);
|
||||
}
|
||||
if (tryBuildAnyStructure(t, extraBuildings))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BuildingID BuildingManager::getMaxPossibleGoldBuilding(const CGTownInstance * t)
|
||||
{
|
||||
if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
|
||||
return BuildingID::CAPITOL;
|
||||
else if(cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
|
||||
return BuildingID::CITY_HALL;
|
||||
else if(cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
|
||||
return BuildingID::TOWN_HALL;
|
||||
else
|
||||
return BuildingID::VILLAGE_HALL;
|
||||
}
|
||||
|
||||
boost::optional<PotentialBuilding> BuildingManager::immediateBuilding() const
|
||||
{
|
||||
if (immediateBuildings.size())
|
||||
return boost::optional<PotentialBuilding>(immediateBuildings.front()); //back? whatever
|
||||
else
|
||||
return boost::optional<PotentialBuilding>();
|
||||
}
|
||||
|
||||
boost::optional<PotentialBuilding> BuildingManager::expensiveBuilding() const
|
||||
{
|
||||
if (expensiveBuildings.size())
|
||||
return boost::optional<PotentialBuilding>(expensiveBuildings.front());
|
||||
else
|
||||
return boost::optional<PotentialBuilding>();
|
||||
}
|
75
AI/Nullkiller/BuildingManager.h
Normal file
75
AI/Nullkiller/BuildingManager.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* BuildingManager.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AIUtility.h"
|
||||
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "VCAI.h"
|
||||
|
||||
struct DLL_EXPORT PotentialBuilding
|
||||
{
|
||||
BuildingID bid;
|
||||
TResources price;
|
||||
//days to build?
|
||||
};
|
||||
|
||||
class DLL_EXPORT IBuildingManager //: public: IAbstractManager
|
||||
{ //info about town development
|
||||
public:
|
||||
virtual ~IBuildingManager() = default;
|
||||
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
||||
virtual void setAI(VCAI * AI) = 0;
|
||||
|
||||
virtual bool getBuildingOptions(const CGTownInstance * t) = 0;
|
||||
virtual boost::optional<PotentialBuilding> immediateBuilding() const = 0;
|
||||
virtual boost::optional<PotentialBuilding> expensiveBuilding() const = 0;
|
||||
virtual boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT BuildingManager : public IBuildingManager
|
||||
{
|
||||
friend class VCAI;
|
||||
friend class AIhelper;
|
||||
friend struct SetGlobalState;
|
||||
|
||||
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
|
||||
VCAI * ai;
|
||||
|
||||
public:
|
||||
|
||||
//try build anything in given town, and execute resulting Goal if any
|
||||
bool getBuildingOptions(const CGTownInstance * t) override;
|
||||
BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
|
||||
boost::optional<PotentialBuilding> immediateBuilding() const override;
|
||||
boost::optional<PotentialBuilding> expensiveBuilding() const override;
|
||||
boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
|
||||
|
||||
protected:
|
||||
|
||||
bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
|
||||
//try build first unbuilt structure
|
||||
bool tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7);
|
||||
//try build ANY unbuilt structure
|
||||
|
||||
bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
|
||||
|
||||
private:
|
||||
//TODO: remember current town?
|
||||
std::vector<PotentialBuilding> immediateBuildings; //what we can build right now in current town
|
||||
std::vector<PotentialBuilding> expensiveBuildings; //what we coudl build but can't afford
|
||||
|
||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
};
|
127
AI/Nullkiller/CMakeLists.txt
Normal file
127
AI/Nullkiller/CMakeLists.txt
Normal file
@ -0,0 +1,127 @@
|
||||
if(FL_FOUND)
|
||||
include_directories(${FL_INCLUDE_DIRS})
|
||||
else()
|
||||
include_directories(${CMAKE_HOME_DIRECTORY}/AI/FuzzyLite/fuzzylite)
|
||||
endif()
|
||||
include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
|
||||
|
||||
set(VCAI_SRCS
|
||||
StdInc.cpp
|
||||
|
||||
Pathfinding/AIPathfinderConfig.cpp
|
||||
Pathfinding/AIPathfinder.cpp
|
||||
Pathfinding/AINodeStorage.cpp
|
||||
Pathfinding/PathfindingManager.cpp
|
||||
Pathfinding/Actions/BattleAction.cpp
|
||||
Pathfinding/Actions/BoatActions.cpp
|
||||
Pathfinding/Actions/TownPortalAction.cpp
|
||||
Pathfinding/Rules/AILayerTransitionRule.cpp
|
||||
Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
|
||||
Pathfinding/Rules/AIMovementToDestinationRule.cpp
|
||||
Pathfinding/Rules/AIPreviousNodeRule.cpp
|
||||
AIUtility.cpp
|
||||
AIhelper.cpp
|
||||
ResourceManager.cpp
|
||||
BuildingManager.cpp
|
||||
SectorMap.cpp
|
||||
BuildingManager.cpp
|
||||
MapObjectsEvaluator.cpp
|
||||
FuzzyEngines.cpp
|
||||
FuzzyHelper.cpp
|
||||
Goals/AbstractGoal.cpp
|
||||
Goals/BuildBoat.cpp
|
||||
Goals/Build.cpp
|
||||
Goals/BuildThis.cpp
|
||||
Goals/Explore.cpp
|
||||
Goals/GatherArmy.cpp
|
||||
Goals/GatherTroops.cpp
|
||||
Goals/BuyArmy.cpp
|
||||
Goals/AdventureSpellCast.cpp
|
||||
Goals/Win.cpp
|
||||
Goals/VisitTile.cpp
|
||||
Goals/VisitObj.cpp
|
||||
Goals/VisitHero.cpp
|
||||
Goals/CollectRes.cpp
|
||||
Goals/Trade.cpp
|
||||
Goals/RecruitHero.cpp
|
||||
Goals/Conquer.cpp
|
||||
Goals/ClearWayTo.cpp
|
||||
Goals/DigAtTile.cpp
|
||||
Goals/GetArtOfType.cpp
|
||||
Goals/FindObj.cpp
|
||||
Goals/CompleteQuest.cpp
|
||||
main.cpp
|
||||
VCAI.cpp
|
||||
)
|
||||
|
||||
set(VCAI_HEADERS
|
||||
StdInc.h
|
||||
|
||||
Pathfinding/AIPathfinderConfig.h
|
||||
Pathfinding/AIPathfinder.h
|
||||
Pathfinding/AINodeStorage.h
|
||||
Pathfinding/PathfindingManager.h
|
||||
Pathfinding/Actions/ISpecialAction.h
|
||||
Pathfinding/Actions/BattleAction.h
|
||||
Pathfinding/Actions/BoatActions.h
|
||||
Pathfinding/Actions/TownPortalAction.h
|
||||
Pathfinding/Rules/AILayerTransitionRule.h
|
||||
Pathfinding/Rules/AIMovementAfterDestinationRule.h
|
||||
Pathfinding/Rules/AIMovementToDestinationRule.h
|
||||
Pathfinding/Rules/AIPreviousNodeRule.h
|
||||
AIUtility.h
|
||||
AIhelper.h
|
||||
ResourceManager.h
|
||||
BuildingManager.h
|
||||
SectorMap.h
|
||||
BuildingManager.h
|
||||
MapObjectsEvaluator.h
|
||||
FuzzyEngines.h
|
||||
FuzzyHelper.h
|
||||
Goals/AbstractGoal.h
|
||||
Goals/CGoal.h
|
||||
Goals/Invalid.h
|
||||
Goals/BuildBoat.h
|
||||
Goals/Build.h
|
||||
Goals/BuildThis.h
|
||||
Goals/Explore.h
|
||||
Goals/GatherArmy.h
|
||||
Goals/GatherTroops.h
|
||||
Goals/BuyArmy.h
|
||||
Goals/AdventureSpellCast.h
|
||||
Goals/Win.h
|
||||
Goals/VisitTile.h
|
||||
Goals/VisitObj.h
|
||||
Goals/VisitHero.h
|
||||
Goals/CollectRes.h
|
||||
Goals/Trade.h
|
||||
Goals/RecruitHero.h
|
||||
Goals/Conquer.h
|
||||
Goals/ClearWayTo.h
|
||||
Goals/DigAtTile.h
|
||||
Goals/GetArtOfType.h
|
||||
Goals/FindObj.h
|
||||
Goals/CompleteQuest.h
|
||||
Goals/Goals.h
|
||||
VCAI.h
|
||||
)
|
||||
|
||||
assign_source_group(${VCAI_SRCS} ${VCAI_HEADERS})
|
||||
|
||||
if(ANDROID) # android compiles ai libs into main lib directly, so we skip this library and just reuse sources list
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(VCAI SHARED ${VCAI_SRCS} ${VCAI_HEADERS})
|
||||
if(FL_FOUND)
|
||||
target_link_libraries(VCAI ${FL_LIBRARIES} vcmi)
|
||||
else()
|
||||
target_link_libraries(VCAI fl-static vcmi)
|
||||
endif()
|
||||
|
||||
vcmi_set_output_dir(VCAI "AI")
|
||||
|
||||
set_target_properties(VCAI PROPERTIES ${PCH_PROPERTIES})
|
||||
cotire(VCAI)
|
||||
|
||||
install(TARGETS VCAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR})
|
458
AI/Nullkiller/FuzzyEngines.cpp
Normal file
458
AI/Nullkiller/FuzzyEngines.cpp
Normal file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* FuzzyEngines.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 "FuzzyEngines.h"
|
||||
#include "Goals/Goals.h"
|
||||
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
#include "VCAI.h"
|
||||
#include "MapObjectsEvaluator.h"
|
||||
|
||||
#define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
|
||||
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
|
||||
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
engineBase::engineBase()
|
||||
{
|
||||
engine.addRuleBlock(&rules);
|
||||
}
|
||||
|
||||
void engineBase::configure()
|
||||
{
|
||||
engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid", "Proportional");
|
||||
logAi->info(engine.toString());
|
||||
}
|
||||
|
||||
void engineBase::addRule(const std::string & txt)
|
||||
{
|
||||
rules.addRule(fl::Rule::parse(txt, &engine));
|
||||
}
|
||||
|
||||
struct armyStructure
|
||||
{
|
||||
float walkers, shooters, flyers;
|
||||
ui32 maxSpeed;
|
||||
};
|
||||
|
||||
armyStructure evaluateArmyStructure(const CArmedInstance * army)
|
||||
{
|
||||
ui64 totalStrenght = army->getArmyStrength();
|
||||
double walkersStrenght = 0;
|
||||
double flyersStrenght = 0;
|
||||
double shootersStrenght = 0;
|
||||
ui32 maxSpeed = 0;
|
||||
|
||||
static const CSelector selectorSHOOTER = Selector::type(Bonus::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)Bonus::SHOOTER);
|
||||
|
||||
static const CSelector selectorFLYING = Selector::type(Bonus::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)Bonus::FLYING);
|
||||
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type(Bonus::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)Bonus::STACKS_SPEED);
|
||||
|
||||
for(auto s : army->Slots())
|
||||
{
|
||||
bool walker = true;
|
||||
const CCreature * creature = s.second->type;
|
||||
if(creature->hasBonus(selectorSHOOTER, keySHOOTER))
|
||||
{
|
||||
shootersStrenght += s.second->getPower();
|
||||
walker = false;
|
||||
}
|
||||
if(creature->hasBonus(selectorFLYING, keyFLYING))
|
||||
{
|
||||
flyersStrenght += s.second->getPower();
|
||||
walker = false;
|
||||
}
|
||||
if(walker)
|
||||
walkersStrenght += s.second->getPower();
|
||||
|
||||
vstd::amax(maxSpeed, creature->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED));
|
||||
}
|
||||
armyStructure as;
|
||||
as.walkers = walkersStrenght / totalStrenght;
|
||||
as.shooters = shootersStrenght / totalStrenght;
|
||||
as.flyers = flyersStrenght / totalStrenght;
|
||||
as.maxSpeed = maxSpeed;
|
||||
assert(as.walkers || as.flyers || as.shooters);
|
||||
return as;
|
||||
}
|
||||
|
||||
float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const
|
||||
{
|
||||
if(goal.evaluationContext.movementCost > 0)
|
||||
{
|
||||
return goal.evaluationContext.movementCost;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile);
|
||||
return pathInfo->cost;
|
||||
}
|
||||
}
|
||||
|
||||
TacticalAdvantageEngine::TacticalAdvantageEngine()
|
||||
{
|
||||
try
|
||||
{
|
||||
ourShooters = new fl::InputVariable("OurShooters");
|
||||
ourWalkers = new fl::InputVariable("OurWalkers");
|
||||
ourFlyers = new fl::InputVariable("OurFlyers");
|
||||
enemyShooters = new fl::InputVariable("EnemyShooters");
|
||||
enemyWalkers = new fl::InputVariable("EnemyWalkers");
|
||||
enemyFlyers = new fl::InputVariable("EnemyFlyers");
|
||||
|
||||
//Tactical advantage calculation
|
||||
std::vector<fl::InputVariable *> helper =
|
||||
{
|
||||
ourShooters, ourWalkers, ourFlyers, enemyShooters, enemyWalkers, enemyFlyers
|
||||
};
|
||||
|
||||
for(auto val : helper)
|
||||
{
|
||||
engine.addInputVariable(val);
|
||||
val->addTerm(new fl::Ramp("FEW", 0.6, 0.0));
|
||||
val->addTerm(new fl::Ramp("MANY", 0.4, 1));
|
||||
val->setRange(0.0, 1.0);
|
||||
}
|
||||
|
||||
ourSpeed = new fl::InputVariable("OurSpeed");
|
||||
enemySpeed = new fl::InputVariable("EnemySpeed");
|
||||
|
||||
helper = { ourSpeed, enemySpeed };
|
||||
|
||||
for(auto val : helper)
|
||||
{
|
||||
engine.addInputVariable(val);
|
||||
val->addTerm(new fl::Ramp("LOW", 6.5, 3));
|
||||
val->addTerm(new fl::Triangle("MEDIUM", 5.5, 10.5));
|
||||
val->addTerm(new fl::Ramp("HIGH", 8.5, 16));
|
||||
val->setRange(0, 25);
|
||||
}
|
||||
|
||||
castleWalls = new fl::InputVariable("CastleWalls");
|
||||
engine.addInputVariable(castleWalls);
|
||||
{
|
||||
fl::Rectangle * none = new fl::Rectangle("NONE", CGTownInstance::NONE, CGTownInstance::NONE + (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f);
|
||||
castleWalls->addTerm(none);
|
||||
|
||||
fl::Trapezoid * medium = new fl::Trapezoid("MEDIUM", (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f, CGTownInstance::FORT,
|
||||
CGTownInstance::CITADEL, CGTownInstance::CITADEL + (CGTownInstance::CASTLE - CGTownInstance::CITADEL) * 0.5f);
|
||||
castleWalls->addTerm(medium);
|
||||
|
||||
fl::Ramp * high = new fl::Ramp("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE);
|
||||
castleWalls->addTerm(high);
|
||||
|
||||
castleWalls->setRange(CGTownInstance::NONE, CGTownInstance::CASTLE);
|
||||
}
|
||||
|
||||
|
||||
bankPresent = new fl::InputVariable("Bank");
|
||||
engine.addInputVariable(bankPresent);
|
||||
{
|
||||
fl::Rectangle * termFalse = new fl::Rectangle("FALSE", 0.0, 0.5f);
|
||||
bankPresent->addTerm(termFalse);
|
||||
fl::Rectangle * termTrue = new fl::Rectangle("TRUE", 0.5f, 1);
|
||||
bankPresent->addTerm(termTrue);
|
||||
bankPresent->setRange(0, 1);
|
||||
}
|
||||
|
||||
threat = new fl::OutputVariable("Threat");
|
||||
engine.addOutputVariable(threat);
|
||||
threat->addTerm(new fl::Ramp("LOW", 1, MIN_AI_STRENGTH));
|
||||
threat->addTerm(new fl::Triangle("MEDIUM", 0.8, 1.2));
|
||||
threat->addTerm(new fl::Ramp("HIGH", 1, 1.5));
|
||||
threat->setRange(MIN_AI_STRENGTH, 1.5);
|
||||
|
||||
addRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW");
|
||||
addRule("if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW");
|
||||
addRule("if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH");
|
||||
addRule("if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW");
|
||||
|
||||
addRule("if OurWalkers is FEW and EnemyShooters is MANY then Threat is LOW");
|
||||
addRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is HIGH");
|
||||
//just to cover all cases
|
||||
addRule("if OurShooters is FEW and EnemySpeed is HIGH then Threat is MEDIUM");
|
||||
addRule("if EnemySpeed is MEDIUM then Threat is MEDIUM");
|
||||
addRule("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM");
|
||||
|
||||
addRule("if Bank is TRUE and OurShooters is MANY then Threat is HIGH");
|
||||
addRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW");
|
||||
|
||||
addRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is HIGH");
|
||||
addRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM");
|
||||
addRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW");
|
||||
|
||||
}
|
||||
catch(fl::Exception & pe)
|
||||
{
|
||||
logAi->error("initTacticalAdvantage: %s", pe.getWhat());
|
||||
}
|
||||
configure();
|
||||
}
|
||||
|
||||
float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy)
|
||||
{
|
||||
float output = 1;
|
||||
/*try //TODO: rework this engine, it tends to produce nonsense output
|
||||
{
|
||||
armyStructure ourStructure = evaluateArmyStructure(we);
|
||||
armyStructure enemyStructure = evaluateArmyStructure(enemy);
|
||||
|
||||
ourWalkers->setValue(ourStructure.walkers);
|
||||
ourShooters->setValue(ourStructure.shooters);
|
||||
ourFlyers->setValue(ourStructure.flyers);
|
||||
ourSpeed->setValue(ourStructure.maxSpeed);
|
||||
|
||||
enemyWalkers->setValue(enemyStructure.walkers);
|
||||
enemyShooters->setValue(enemyStructure.shooters);
|
||||
enemyFlyers->setValue(enemyStructure.flyers);
|
||||
enemySpeed->setValue(enemyStructure.maxSpeed);
|
||||
|
||||
bool bank = dynamic_cast<const CBank *>(enemy);
|
||||
if(bank)
|
||||
bankPresent->setValue(1);
|
||||
else
|
||||
bankPresent->setValue(0);
|
||||
|
||||
const CGTownInstance * fort = dynamic_cast<const CGTownInstance *>(enemy);
|
||||
if(fort)
|
||||
castleWalls->setValue(fort->fortLevel());
|
||||
else
|
||||
castleWalls->setValue(0);
|
||||
|
||||
engine.process();
|
||||
output = threat->getValue();
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("getTacticalAdvantage: %s ", fe.getWhat());
|
||||
}
|
||||
|
||||
if(output < 0 || (output != output))
|
||||
{
|
||||
fl::InputVariable * tab[] = { bankPresent, castleWalls, ourWalkers, ourShooters, ourFlyers, ourSpeed, enemyWalkers, enemyShooters, enemyFlyers, enemySpeed };
|
||||
std::string names[] = { "bankPresent", "castleWalls", "ourWalkers", "ourShooters", "ourFlyers", "ourSpeed", "enemyWalkers", "enemyShooters", "enemyFlyers", "enemySpeed" };
|
||||
std::stringstream log("Warning! Fuzzy engine doesn't cover this set of parameters: ");
|
||||
|
||||
for(int i = 0; i < boost::size(tab); i++)
|
||||
log << names[i] << ": " << tab[i]->getValue() << " ";
|
||||
logAi->error(log.str());
|
||||
assert(false);
|
||||
}*/
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec)
|
||||
|
||||
HeroMovementGoalEngineBase::HeroMovementGoalEngineBase()
|
||||
{
|
||||
try
|
||||
{
|
||||
strengthRatio = new fl::InputVariable("strengthRatio"); //hero must be strong enough to defeat guards
|
||||
heroStrength = new fl::InputVariable("heroStrength"); //we want to use weakest possible hero
|
||||
turnDistance = new fl::InputVariable("turnDistance"); //we want to use hero who is near
|
||||
missionImportance = new fl::InputVariable("lockedMissionImportance"); //we may want to preempt hero with low-priority mission
|
||||
value = new fl::OutputVariable("Value");
|
||||
value->setMinimum(0);
|
||||
value->setMaximum(5);
|
||||
|
||||
std::vector<fl::InputVariable *> helper = { strengthRatio, heroStrength, turnDistance, missionImportance };
|
||||
for(auto val : helper)
|
||||
{
|
||||
engine.addInputVariable(val);
|
||||
}
|
||||
engine.addOutputVariable(value);
|
||||
|
||||
strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0));
|
||||
strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3));
|
||||
strengthRatio->setRange(0, SAFE_ATTACK_CONSTANT * 3);
|
||||
|
||||
//strength compared to our main hero
|
||||
heroStrength->addTerm(new fl::Ramp("LOW", 0.5, 0));
|
||||
heroStrength->addTerm(new fl::Triangle("MEDIUM", 0.2, 0.8));
|
||||
heroStrength->addTerm(new fl::Ramp("HIGH", 0.5, 1));
|
||||
heroStrength->setRange(0.0, 1.0);
|
||||
|
||||
turnDistance->addTerm(new fl::Ramp("SHORT", 0.5, 0));
|
||||
turnDistance->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8));
|
||||
turnDistance->addTerm(new fl::Ramp("LONG", 0.5, 10));
|
||||
turnDistance->setRange(0.0, 10.0);
|
||||
|
||||
missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0));
|
||||
missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3));
|
||||
missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5));
|
||||
missionImportance->setRange(0.0, 5.0);
|
||||
|
||||
//an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/
|
||||
//should be same as "mission Importance" to keep consistency
|
||||
value->addTerm(new fl::Ramp("LOW", 2.5, 0));
|
||||
value->addTerm(new fl::Triangle("MEDIUM", 2, 3)); //can't be center of mass :/
|
||||
value->addTerm(new fl::Ramp("HIGH", 2.5, 5));
|
||||
value->setRange(0.0, 5.0);
|
||||
|
||||
//use unarmed scouts if possible
|
||||
addRule("if strengthRatio is HIGH and heroStrength is LOW then Value is HIGH");
|
||||
//we may want to use secondary hero(es) rather than main hero
|
||||
addRule("if strengthRatio is HIGH and heroStrength is MEDIUM then Value is MEDIUM");
|
||||
addRule("if strengthRatio is HIGH and heroStrength is HIGH then Value is LOW");
|
||||
//don't assign targets to heroes who are too weak, but prefer targets of our main hero (in case we need to gather army)
|
||||
addRule("if strengthRatio is LOW and heroStrength is LOW then Value is LOW");
|
||||
//attempt to arm secondary heroes is not stupid
|
||||
addRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is HIGH");
|
||||
addRule("if strengthRatio is LOW and heroStrength is HIGH then Value is LOW");
|
||||
|
||||
//do not cancel important goals
|
||||
addRule("if lockedMissionImportance is HIGH then Value is LOW");
|
||||
addRule("if lockedMissionImportance is MEDIUM then Value is MEDIUM");
|
||||
addRule("if lockedMissionImportance is LOW then Value is HIGH");
|
||||
//pick nearby objects if it's easy, avoid long walks
|
||||
addRule("if turnDistance is SHORT then Value is HIGH");
|
||||
addRule("if turnDistance is MEDIUM then Value is MEDIUM");
|
||||
addRule("if turnDistance is LONG then Value is LOW");
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("HeroMovementGoalEngineBase: %s", fe.getWhat());
|
||||
}
|
||||
}
|
||||
|
||||
void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & goal)
|
||||
{
|
||||
float turns = calculateTurnDistanceInputValue(goal);
|
||||
float missionImportanceData = 0;
|
||||
|
||||
if(vstd::contains(ai->lockedHeroes, goal.hero))
|
||||
{
|
||||
missionImportanceData = ai->lockedHeroes[goal.hero]->priority;
|
||||
}
|
||||
else if(goal.parent)
|
||||
{
|
||||
missionImportanceData = goal.parent->priority;
|
||||
}
|
||||
|
||||
float strengthRatioData = 10.0f; //we are much stronger than enemy
|
||||
ui64 danger = fh->evaluateDanger(goal.tile, goal.hero.h);
|
||||
if(danger)
|
||||
strengthRatioData = (fl::scalar)goal.hero.h->getTotalStrength() / danger;
|
||||
|
||||
try
|
||||
{
|
||||
strengthRatio->setValue(strengthRatioData);
|
||||
heroStrength->setValue((fl::scalar)goal.hero->getTotalStrength() / ai->primaryHero()->getTotalStrength());
|
||||
turnDistance->setValue(turns);
|
||||
missionImportance->setValue(missionImportanceData);
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("HeroMovementGoalEngineBase::setSharedFuzzyVariables: %s", fe.getWhat());
|
||||
}
|
||||
}
|
||||
|
||||
VisitObjEngine::VisitObjEngine()
|
||||
{
|
||||
try
|
||||
{
|
||||
objectValue = new fl::InputVariable("objectValue"); //value of that object type known by AI
|
||||
|
||||
engine.addInputVariable(objectValue);
|
||||
|
||||
//objectValue ranges are based on checking RMG priorities of some objects and checking LOW/MID/HIGH proportions for various values in QtFuzzyLite
|
||||
objectValue->addTerm(new fl::Ramp("LOW", 3500.0, 0.0));
|
||||
objectValue->addTerm(new fl::Triangle("MEDIUM", 0.0, 8500.0));
|
||||
std::vector<fl::Discrete::Pair> multiRamp = { fl::Discrete::Pair(5000.0, 0.0), fl::Discrete::Pair(10000.0, 0.75), fl::Discrete::Pair(20000.0, 1.0) };
|
||||
objectValue->addTerm(new fl::Discrete("HIGH", multiRamp));
|
||||
objectValue->setRange(0.0, 20000.0); //relic artifact value is border value by design, even better things are scaled down.
|
||||
|
||||
addRule("if objectValue is HIGH then Value is HIGH");
|
||||
addRule("if objectValue is MEDIUM then Value is MEDIUM");
|
||||
addRule("if objectValue is LOW then Value is LOW");
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("FindWanderTarget: %s", fe.getWhat());
|
||||
}
|
||||
configure();
|
||||
}
|
||||
|
||||
float VisitObjEngine::evaluate(Goals::VisitObj & goal)
|
||||
{
|
||||
if(!goal.hero)
|
||||
return 0;
|
||||
|
||||
auto obj = ai->myCb->getObj(ObjectInstanceID(goal.objid));
|
||||
if(!obj)
|
||||
{
|
||||
logAi->error("Goals::VisitObj objid " + std::to_string(goal.objid) + " no longer visible, probably goal used for something it's not intended");
|
||||
return -100; // FIXME: Added check when goal was used for hero instead of VisitHero, but crashes are bad anyway
|
||||
}
|
||||
|
||||
boost::optional<int> objValueKnownByAI = MapObjectsEvaluator::getInstance().getObjectValue(obj);
|
||||
int objValue = 0;
|
||||
|
||||
if(objValueKnownByAI != boost::none) //consider adding value manipulation based on object instances on map
|
||||
{
|
||||
objValue = std::min(std::max(objValueKnownByAI.get(), 0), 20000);
|
||||
}
|
||||
else
|
||||
{
|
||||
MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0);
|
||||
logGlobal->error("AI met object type it doesn't know - ID: " + std::to_string(obj->ID) + ", subID: " + std::to_string(obj->subID) + " - adding to database with value " + std::to_string(objValue));
|
||||
}
|
||||
|
||||
setSharedFuzzyVariables(goal);
|
||||
|
||||
float output = -1.0f;
|
||||
try
|
||||
{
|
||||
objectValue->setValue(objValue);
|
||||
engine.process();
|
||||
output = value->getValue();
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("evaluate getWanderTargetObjectValue: %s", fe.getWhat());
|
||||
}
|
||||
assert(output >= 0.0f);
|
||||
return output;
|
||||
}
|
||||
|
||||
VisitTileEngine::VisitTileEngine() //so far no VisitTile-specific variables that are not shared with HeroMovementGoalEngineBase
|
||||
{
|
||||
configure();
|
||||
}
|
||||
|
||||
float VisitTileEngine::evaluate(Goals::VisitTile & goal)
|
||||
{
|
||||
//we assume that hero is already set and we want to choose most suitable one for the mission
|
||||
if(!goal.hero)
|
||||
return 0;
|
||||
|
||||
//assert(cb->isInTheMap(g.tile));
|
||||
|
||||
setSharedFuzzyVariables(goal);
|
||||
|
||||
try
|
||||
{
|
||||
engine.process();
|
||||
|
||||
goal.priority = value->getValue();
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("evaluate VisitTile: %s", fe.getWhat());
|
||||
}
|
||||
assert(goal.priority >= 0);
|
||||
return goal.priority;
|
||||
}
|
72
AI/Nullkiller/FuzzyEngines.h
Normal file
72
AI/Nullkiller/FuzzyEngines.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* FuzzyEngines.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 <fl/Headers.h>
|
||||
#include "Goals/AbstractGoal.h"
|
||||
|
||||
class CArmedInstance;
|
||||
|
||||
class engineBase //subclasses create fuzzylite variables with "new" that are not freed - this is desired as fl::Engine wants to destroy these...
|
||||
{
|
||||
protected:
|
||||
fl::Engine engine;
|
||||
fl::RuleBlock rules;
|
||||
virtual void configure();
|
||||
void addRule(const std::string & txt);
|
||||
public:
|
||||
engineBase();
|
||||
};
|
||||
|
||||
class TacticalAdvantageEngine : public engineBase //TODO: rework this engine, it does not work well (example: AI hero with 140 beholders attacked 150 beholders - engine lowered danger 50000 -> 35000)
|
||||
{
|
||||
public:
|
||||
TacticalAdvantageEngine();
|
||||
float getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy); //returns factor how many times enemy is stronger than us
|
||||
private:
|
||||
fl::InputVariable * ourWalkers, *ourShooters, *ourFlyers, *enemyWalkers, *enemyShooters, *enemyFlyers;
|
||||
fl::InputVariable * ourSpeed, *enemySpeed;
|
||||
fl::InputVariable * bankPresent;
|
||||
fl::InputVariable * castleWalls;
|
||||
fl::OutputVariable * threat;
|
||||
};
|
||||
|
||||
class HeroMovementGoalEngineBase : public engineBase //in future - maybe derive from some (GoalEngineBase : public engineBase) class for handling non-movement goals with common utility for goal engines
|
||||
{
|
||||
public:
|
||||
HeroMovementGoalEngineBase();
|
||||
|
||||
protected:
|
||||
void setSharedFuzzyVariables(Goals::AbstractGoal & goal);
|
||||
|
||||
fl::InputVariable * strengthRatio;
|
||||
fl::InputVariable * heroStrength;
|
||||
fl::InputVariable * turnDistance;
|
||||
fl::InputVariable * missionImportance;
|
||||
fl::OutputVariable * value;
|
||||
|
||||
private:
|
||||
float calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const;
|
||||
};
|
||||
|
||||
class VisitTileEngine : public HeroMovementGoalEngineBase
|
||||
{
|
||||
public:
|
||||
VisitTileEngine();
|
||||
float evaluate(Goals::VisitTile & goal);
|
||||
};
|
||||
|
||||
class VisitObjEngine : public HeroMovementGoalEngineBase
|
||||
{
|
||||
public:
|
||||
VisitObjEngine();
|
||||
float evaluate(Goals::VisitObj & goal);
|
||||
protected:
|
||||
fl::InputVariable * objectValue;
|
||||
};
|
333
AI/Nullkiller/FuzzyHelper.cpp
Normal file
333
AI/Nullkiller/FuzzyHelper.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* FuzzyHelper.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 "FuzzyHelper.h"
|
||||
|
||||
#include "../../lib/mapObjects/CommonConstructors.h"
|
||||
#include "Goals/Goals.h"
|
||||
#include "VCAI.h"
|
||||
|
||||
FuzzyHelper * fh;
|
||||
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
|
||||
Goals::TSubgoal FuzzyHelper::chooseSolution(Goals::TGoalVec vec)
|
||||
{
|
||||
if(vec.empty())
|
||||
{
|
||||
logAi->debug("FuzzyHelper found no goals. Returning Goals::Invalid.");
|
||||
|
||||
//no possibilities found
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
//a trick to switch between heroes less often - calculatePaths is costly
|
||||
auto sortByHeroes = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
|
||||
{
|
||||
return lhs->hero.h < rhs->hero.h;
|
||||
};
|
||||
boost::sort(vec, sortByHeroes);
|
||||
|
||||
for(auto g : vec)
|
||||
{
|
||||
setPriority(g);
|
||||
}
|
||||
|
||||
auto compareGoals = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
|
||||
{
|
||||
return lhs->priority < rhs->priority;
|
||||
};
|
||||
|
||||
for(auto goal : vec)
|
||||
{
|
||||
logAi->trace("FuzzyHelper evaluated goal %s, priority=%.4f", goal->name(), goal->priority);
|
||||
}
|
||||
|
||||
Goals::TSubgoal result = *boost::max_element(vec, compareGoals);
|
||||
|
||||
logAi->debug("FuzzyHelper returned goal %s, priority=%.4f", result->name(), result->priority);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
|
||||
{
|
||||
//this one is not fuzzy anymore, just calculate weighted average
|
||||
|
||||
auto objectInfo = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance);
|
||||
|
||||
CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
|
||||
|
||||
ui64 totalStrength = 0;
|
||||
ui8 totalChance = 0;
|
||||
for(auto config : bankInfo->getPossibleGuards())
|
||||
{
|
||||
totalStrength += config.second.totalStrength * config.first;
|
||||
totalChance += config.first;
|
||||
}
|
||||
return totalStrength / std::max<ui8>(totalChance, 1); //avoid division by zero
|
||||
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::VisitTile & g)
|
||||
{
|
||||
if(g.parent)
|
||||
{
|
||||
g.parent->accept(this);
|
||||
}
|
||||
|
||||
return visitTileEngine.evaluate(g);
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::BuildBoat & g)
|
||||
{
|
||||
const float buildBoatPenalty = 0.25;
|
||||
|
||||
if(!g.parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g.parent->accept(this) - buildBoatPenalty;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::AdventureSpellCast & g)
|
||||
{
|
||||
if(!g.parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const CSpell * spell = g.getSpell();
|
||||
const float spellCastPenalty = (float)g.hero->getSpellCost(spell) / g.hero->mana;
|
||||
|
||||
return g.parent->accept(this) - spellCastPenalty;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::CompleteQuest & g)
|
||||
{
|
||||
// TODO: How to evaluate quest complexity?
|
||||
const float questPenalty = 0.2;
|
||||
|
||||
if(!g.parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g.parent->accept(this) * questPenalty;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::VisitObj & g)
|
||||
{
|
||||
if(g.parent)
|
||||
{
|
||||
g.parent->accept(this);
|
||||
}
|
||||
|
||||
return visitObjEngine.evaluate(g);
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::VisitHero & g)
|
||||
{
|
||||
auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
|
||||
if(!obj)
|
||||
return -100; //hero died in the meantime
|
||||
else
|
||||
{
|
||||
g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).accept(this));
|
||||
}
|
||||
return g.priority;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::GatherArmy & g)
|
||||
{
|
||||
//the more army we need, the more important goal
|
||||
//the more army we lack, the less important goal
|
||||
float army = g.hero->getArmyStrength();
|
||||
float ratio = g.value / std::max(g.value - army, 2000.0f); //2000 is about the value of hero recruited from tavern
|
||||
return 5 * (ratio / (ratio + 2)); //so 50% army gives 2.5, asymptotic 5
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::ClearWayTo & g)
|
||||
{
|
||||
if (!g.hero.h)
|
||||
return 0; //lowest priority
|
||||
|
||||
return g.whatToDoToAchieve()->accept(this);
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::BuildThis & g)
|
||||
{
|
||||
return g.priority; //TODO
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::DigAtTile & g)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::CollectRes & g)
|
||||
{
|
||||
return g.priority; //handled by ResourceManager
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::Build & g)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::BuyArmy & g)
|
||||
{
|
||||
return g.priority;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::Explore & g)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::RecruitHero & g)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::Invalid & g)
|
||||
{
|
||||
return -1e10;
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::AbstractGoal & g)
|
||||
{
|
||||
logAi->warn("Cannot evaluate goal %s", g.name());
|
||||
return g.priority;
|
||||
}
|
||||
void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pattern
|
||||
{
|
||||
g->setpriority(g->accept(this)); //this enforces returned value is set
|
||||
}
|
||||
|
||||
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
|
||||
{
|
||||
return evaluateDanger(tile, visitor, ai.get());
|
||||
}
|
||||
|
||||
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai)
|
||||
{
|
||||
auto cb = ai->myCb;
|
||||
const TerrainTile * t = cb->getTile(tile, false);
|
||||
if(!t) //we can know about guard but can't check its tile (the edge of fow)
|
||||
return 190000000; //MUCH
|
||||
|
||||
ui64 objectDanger = 0;
|
||||
ui64 guardDanger = 0;
|
||||
|
||||
auto visitableObjects = cb->getVisitableObjs(tile);
|
||||
// in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero.
|
||||
if(vstd::contains_if(visitableObjects, objWithID<Obj::HERO>))
|
||||
{
|
||||
vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj)
|
||||
{
|
||||
return !objWithID<Obj::HERO>(obj);
|
||||
});
|
||||
}
|
||||
|
||||
if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects))
|
||||
{
|
||||
objectDanger = evaluateDanger(dangerousObject, ai); //unguarded objects can also be dangerous or unhandled
|
||||
if(objectDanger)
|
||||
{
|
||||
//TODO: don't downcast objects AI shouldn't know about!
|
||||
auto armedObj = dynamic_cast<const CArmedInstance *>(dangerousObject);
|
||||
if(armedObj)
|
||||
{
|
||||
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, armedObj);
|
||||
objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?)
|
||||
}
|
||||
}
|
||||
if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
|
||||
{
|
||||
//check guard on the other side of the gate
|
||||
auto it = ai->knownSubterraneanGates.find(dangerousObject);
|
||||
if(it != ai->knownSubterraneanGates.end())
|
||||
{
|
||||
auto guards = cb->getGuardingCreatures(it->second->visitablePos());
|
||||
for(auto cre : guards)
|
||||
{
|
||||
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
|
||||
|
||||
vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto guards = cb->getGuardingCreatures(tile);
|
||||
for(auto cre : guards)
|
||||
{
|
||||
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
|
||||
|
||||
vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage); //we are interested in strongest monster around
|
||||
}
|
||||
|
||||
//TODO mozna odwiedzic blockvis nie ruszajac straznika
|
||||
return std::max(objectDanger, guardDanger);
|
||||
}
|
||||
|
||||
ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
|
||||
{
|
||||
auto cb = ai->myCb;
|
||||
|
||||
if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat
|
||||
return 0;
|
||||
|
||||
switch(obj->ID)
|
||||
{
|
||||
case Obj::HERO:
|
||||
{
|
||||
InfoAboutHero iah;
|
||||
cb->getHeroInfo(obj, iah);
|
||||
return iah.army.getStrength();
|
||||
}
|
||||
case Obj::TOWN:
|
||||
case Obj::GARRISON:
|
||||
case Obj::GARRISON2:
|
||||
{
|
||||
InfoAboutTown iat;
|
||||
cb->getTownInfo(obj, iat);
|
||||
return iat.army.getStrength();
|
||||
}
|
||||
case Obj::MONSTER:
|
||||
{
|
||||
//TODO!!!!!!!!
|
||||
const CGCreature * cre = dynamic_cast<const CGCreature *>(obj);
|
||||
return cre->getArmyStrength();
|
||||
}
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
case Obj::CREATURE_GENERATOR4:
|
||||
{
|
||||
const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
|
||||
return d->getArmyStrength();
|
||||
}
|
||||
case Obj::MINE:
|
||||
case Obj::ABANDONED_MINE:
|
||||
{
|
||||
const CArmedInstance * a = dynamic_cast<const CArmedInstance *>(obj);
|
||||
return a->getArmyStrength();
|
||||
}
|
||||
case Obj::CRYPT: //crypt
|
||||
case Obj::CREATURE_BANK: //crebank
|
||||
case Obj::DRAGON_UTOPIA:
|
||||
case Obj::SHIPWRECK: //shipwreck
|
||||
case Obj::DERELICT_SHIP: //derelict ship
|
||||
// case Obj::PYRAMID:
|
||||
return estimateBankDanger(dynamic_cast<const CBank *>(obj));
|
||||
case Obj::PYRAMID:
|
||||
{
|
||||
if(obj->subID == 0)
|
||||
return estimateBankDanger(dynamic_cast<const CBank *>(obj));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
49
AI/Nullkiller/FuzzyHelper.h
Normal file
49
AI/Nullkiller/FuzzyHelper.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* FuzzyHelper.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 "FuzzyEngines.h"
|
||||
|
||||
class CBank;
|
||||
|
||||
class DLL_EXPORT FuzzyHelper
|
||||
{
|
||||
public:
|
||||
TacticalAdvantageEngine tacticalAdvantageEngine;
|
||||
VisitTileEngine visitTileEngine;
|
||||
VisitObjEngine visitObjEngine;
|
||||
|
||||
float evaluate(Goals::Explore & g);
|
||||
float evaluate(Goals::RecruitHero & g);
|
||||
float evaluate(Goals::VisitTile & g);
|
||||
float evaluate(Goals::VisitObj & g);
|
||||
float evaluate(Goals::VisitHero & g);
|
||||
float evaluate(Goals::BuildThis & g);
|
||||
float evaluate(Goals::DigAtTile & g);
|
||||
float evaluate(Goals::CollectRes & g);
|
||||
float evaluate(Goals::Build & g);
|
||||
float evaluate(Goals::BuyArmy & g);
|
||||
float evaluate(Goals::BuildBoat & g);
|
||||
float evaluate(Goals::GatherArmy & g);
|
||||
float evaluate(Goals::ClearWayTo & g);
|
||||
float evaluate(Goals::CompleteQuest & g);
|
||||
float evaluate(Goals::AdventureSpellCast & g);
|
||||
float evaluate(Goals::Invalid & g);
|
||||
float evaluate(Goals::AbstractGoal & g);
|
||||
void setPriority(Goals::TSubgoal & g);
|
||||
|
||||
ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
|
||||
|
||||
Goals::TSubgoal chooseSolution(Goals::TGoalVec vec);
|
||||
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
|
||||
|
||||
ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
|
||||
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai);
|
||||
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
|
||||
};
|
188
AI/Nullkiller/Goals/AbstractGoal.cpp
Normal file
188
AI/Nullkiller/Goals/AbstractGoal.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* AbstractGoal.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 "AbstractGoal.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
TSubgoal Goals::sptr(const AbstractGoal & tmp)
|
||||
{
|
||||
TSubgoal ptr;
|
||||
ptr.reset(tmp.clone());
|
||||
return ptr;
|
||||
}
|
||||
|
||||
std::string AbstractGoal::name() const //TODO: virtualize
|
||||
{
|
||||
std::string desc;
|
||||
switch(goalType)
|
||||
{
|
||||
case INVALID:
|
||||
return "INVALID";
|
||||
case WIN:
|
||||
return "WIN";
|
||||
case CONQUER:
|
||||
return "CONQUER";
|
||||
case BUILD:
|
||||
return "BUILD";
|
||||
case EXPLORE:
|
||||
desc = "EXPLORE";
|
||||
break;
|
||||
case GATHER_ARMY:
|
||||
desc = "GATHER ARMY";
|
||||
break;
|
||||
case BUY_ARMY:
|
||||
return "BUY ARMY";
|
||||
break;
|
||||
case BOOST_HERO:
|
||||
desc = "BOOST_HERO (unsupported)";
|
||||
break;
|
||||
case RECRUIT_HERO:
|
||||
return "RECRUIT HERO";
|
||||
case BUILD_STRUCTURE:
|
||||
return "BUILD STRUCTURE";
|
||||
case COLLECT_RES:
|
||||
desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + boost::lexical_cast<std::string>(value) + ")";
|
||||
break;
|
||||
case TRADE:
|
||||
{
|
||||
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if (obj)
|
||||
desc = (boost::format("TRADE %d of %s at %s") % value % GameConstants::RESOURCE_NAMES[resID] % obj->getObjectName()).str();
|
||||
}
|
||||
break;
|
||||
case GATHER_TROOPS:
|
||||
desc = "GATHER TROOPS";
|
||||
break;
|
||||
case VISIT_OBJ:
|
||||
{
|
||||
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if(obj)
|
||||
desc = "VISIT OBJ " + obj->getObjectName();
|
||||
}
|
||||
break;
|
||||
case FIND_OBJ:
|
||||
desc = "FIND OBJ " + boost::lexical_cast<std::string>(objid);
|
||||
break;
|
||||
case VISIT_HERO:
|
||||
{
|
||||
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if(obj)
|
||||
desc = "VISIT HERO " + obj->getObjectName();
|
||||
}
|
||||
break;
|
||||
case GET_ART_TYPE:
|
||||
desc = "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name();
|
||||
break;
|
||||
case VISIT_TILE:
|
||||
desc = "VISIT TILE " + tile.toString();
|
||||
break;
|
||||
case CLEAR_WAY_TO:
|
||||
desc = "CLEAR WAY TO " + tile.toString();
|
||||
break;
|
||||
case DIG_AT_TILE:
|
||||
desc = "DIG AT TILE " + tile.toString();
|
||||
break;
|
||||
default:
|
||||
return boost::lexical_cast<std::string>(goalType);
|
||||
}
|
||||
if(hero.get(true)) //FIXME: used to crash when we lost hero and failed goal
|
||||
desc += " (" + hero->name + ")";
|
||||
return desc;
|
||||
}
|
||||
|
||||
bool AbstractGoal::operator==(const AbstractGoal & g) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractGoal::operator<(AbstractGoal & g) //for std::unique
|
||||
{
|
||||
//TODO: make sure it gets goals consistent with == operator
|
||||
if (goalType < g.goalType)
|
||||
return true;
|
||||
if (goalType > g.goalType)
|
||||
return false;
|
||||
if (hero < g.hero)
|
||||
return true;
|
||||
if (hero > g.hero)
|
||||
return false;
|
||||
if (tile < g.tile)
|
||||
return true;
|
||||
if (g.tile < tile)
|
||||
return false;
|
||||
if (objid < g.objid)
|
||||
return true;
|
||||
if (objid > g.objid)
|
||||
return false;
|
||||
if (town < g.town)
|
||||
return true;
|
||||
if (town > g.town)
|
||||
return false;
|
||||
if (value < g.value)
|
||||
return true;
|
||||
if (value > g.value)
|
||||
return false;
|
||||
if (priority < g.priority)
|
||||
return true;
|
||||
if (priority > g.priority)
|
||||
return false;
|
||||
if (resID < g.resID)
|
||||
return true;
|
||||
if (resID > g.resID)
|
||||
return false;
|
||||
if (bid < g.bid)
|
||||
return true;
|
||||
if (bid > g.bid)
|
||||
return false;
|
||||
if (aid < g.aid)
|
||||
return true;
|
||||
if (aid > g.aid)
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: find out why the following are not generated automatically on MVS?
|
||||
bool TSubgoal::operator==(const TSubgoal & rhs) const
|
||||
{
|
||||
return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match
|
||||
}
|
||||
|
||||
bool TSubgoal::operator<(const TSubgoal & rhs) const
|
||||
{
|
||||
return get() < rhs.get(); //compae by value
|
||||
}
|
||||
|
||||
bool AbstractGoal::invalid() const
|
||||
{
|
||||
return goalType == EGoals::INVALID;
|
||||
}
|
||||
|
||||
void AbstractGoal::accept(VCAI * ai)
|
||||
{
|
||||
ai->tryRealize(*this);
|
||||
}
|
||||
|
||||
float AbstractGoal::accept(FuzzyHelper * f)
|
||||
{
|
||||
return f->evaluate(*this);
|
||||
}
|
195
AI/Nullkiller/Goals/AbstractGoal.h
Normal file
195
AI/Nullkiller/Goals/AbstractGoal.h
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* AbstractGoal.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 "../../../lib/VCMI_Lib.h"
|
||||
#include "../../../lib/CBuildingHandler.h"
|
||||
#include "../../../lib/CCreatureHandler.h"
|
||||
#include "../../../lib/CTownHandler.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class AbstractGoal;
|
||||
class Explore;
|
||||
class RecruitHero;
|
||||
class VisitTile;
|
||||
class VisitObj;
|
||||
class VisitHero;
|
||||
class BuildThis;
|
||||
class DigAtTile;
|
||||
class CollectRes;
|
||||
class Build;
|
||||
class BuyArmy;
|
||||
class BuildBoat;
|
||||
class GatherArmy;
|
||||
class ClearWayTo;
|
||||
class Invalid;
|
||||
class Trade;
|
||||
class CompleteQuest;
|
||||
class AdventureSpellCast;
|
||||
|
||||
enum EGoals
|
||||
{
|
||||
INVALID = -1,
|
||||
WIN, CONQUER, BUILD, //build needs to get a real reasoning
|
||||
EXPLORE, GATHER_ARMY,
|
||||
BOOST_HERO,
|
||||
RECRUIT_HERO,
|
||||
BUILD_STRUCTURE, //if hero set, then in visited town
|
||||
COLLECT_RES,
|
||||
GATHER_TROOPS, // val of creatures with objid
|
||||
|
||||
VISIT_OBJ, //visit or defeat or collect the object
|
||||
FIND_OBJ, //find and visit any obj with objid + resid //TODO: consider universal subid for various types (aid, bid)
|
||||
VISIT_HERO, //heroes can move around - set goal abstract and track hero every turn
|
||||
|
||||
GET_ART_TYPE,
|
||||
|
||||
VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
|
||||
CLEAR_WAY_TO,
|
||||
DIG_AT_TILE,//elementar with hero on tile
|
||||
BUY_ARMY, //at specific town
|
||||
TRADE, //val resID at object objid
|
||||
BUILD_BOAT,
|
||||
COMPLETE_QUEST,
|
||||
ADVENTURE_SPELL_CAST
|
||||
};
|
||||
|
||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||
{
|
||||
public:
|
||||
bool operator==(const TSubgoal & rhs) const;
|
||||
bool operator<(const TSubgoal & rhs) const;
|
||||
//TODO: serialize?
|
||||
};
|
||||
|
||||
typedef std::vector<TSubgoal> TGoalVec;
|
||||
|
||||
//method chaining + clone pattern
|
||||
#define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;};
|
||||
#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) override { field = rhs; return *this; };
|
||||
|
||||
#if 0
|
||||
#define SETTER
|
||||
#endif // _DEBUG
|
||||
|
||||
enum { LOW_PR = -1 };
|
||||
|
||||
DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
|
||||
|
||||
struct DLL_EXPORT EvaluationContext
|
||||
{
|
||||
float movementCost;
|
||||
int manaCost;
|
||||
uint64_t danger;
|
||||
|
||||
EvaluationContext()
|
||||
: movementCost(0.0),
|
||||
manaCost(0),
|
||||
danger(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_EXPORT AbstractGoal
|
||||
{
|
||||
public:
|
||||
bool isElementar; VSETTER(bool, isElementar)
|
||||
bool isAbstract; VSETTER(bool, isAbstract)
|
||||
float priority; VSETTER(float, priority)
|
||||
int value; VSETTER(int, value)
|
||||
int resID; VSETTER(int, resID)
|
||||
int objid; VSETTER(int, objid)
|
||||
int aid; VSETTER(int, aid)
|
||||
int3 tile; VSETTER(int3, tile)
|
||||
HeroPtr hero; VSETTER(HeroPtr, hero)
|
||||
const CGTownInstance *town; VSETTER(CGTownInstance *, town)
|
||||
int bid; VSETTER(int, bid)
|
||||
TSubgoal parent; VSETTER(TSubgoal, parent)
|
||||
EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
|
||||
|
||||
AbstractGoal(EGoals goal = EGoals::INVALID)
|
||||
: goalType(goal), evaluationContext()
|
||||
{
|
||||
priority = 0;
|
||||
isElementar = false;
|
||||
isAbstract = false;
|
||||
value = 0;
|
||||
aid = -1;
|
||||
resID = -1;
|
||||
objid = -1;
|
||||
tile = int3(-1, -1, -1);
|
||||
town = nullptr;
|
||||
bid = -1;
|
||||
}
|
||||
virtual ~AbstractGoal() {}
|
||||
//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
|
||||
virtual AbstractGoal * clone() const
|
||||
{
|
||||
return const_cast<AbstractGoal *>(this);
|
||||
}
|
||||
virtual TGoalVec getAllPossibleSubgoals()
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
virtual TSubgoal whatToDoToAchieve()
|
||||
{
|
||||
return sptr(AbstractGoal());
|
||||
}
|
||||
|
||||
EGoals goalType;
|
||||
|
||||
virtual std::string name() const;
|
||||
virtual std::string completeMessage() const
|
||||
{
|
||||
return "This goal is unspecified!";
|
||||
}
|
||||
|
||||
bool invalid() const;
|
||||
|
||||
///Visitor pattern
|
||||
//TODO: make accept work for std::shared_ptr... somehow
|
||||
virtual void accept(VCAI * ai); //unhandled goal will report standard error
|
||||
virtual float accept(FuzzyHelper * f);
|
||||
|
||||
virtual bool operator==(const AbstractGoal & g) const;
|
||||
bool operator<(AbstractGoal & g); //final
|
||||
virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
|
||||
{
|
||||
return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
|
||||
}
|
||||
|
||||
bool operator!=(const AbstractGoal & g) const
|
||||
{
|
||||
return !(*this == g);
|
||||
}
|
||||
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & goalType;
|
||||
h & isElementar;
|
||||
h & isAbstract;
|
||||
h & priority;
|
||||
h & value;
|
||||
h & resID;
|
||||
h & objid;
|
||||
h & aid;
|
||||
h & tile;
|
||||
h & hero;
|
||||
h & town;
|
||||
h & bid;
|
||||
}
|
||||
};
|
||||
}
|
81
AI/Nullkiller/Goals/AdventureSpellCast.cpp
Normal file
81
AI/Nullkiller/Goals/AdventureSpellCast.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* AdventureSpellCast.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 "AdventureSpellCast.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../lib/CPathfinder.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const
|
||||
{
|
||||
return hero.h == other.hero.h;
|
||||
}
|
||||
|
||||
TSubgoal AdventureSpellCast::whatToDoToAchieve()
|
||||
{
|
||||
if(!hero.validAndSet())
|
||||
throw cannotFulfillGoalException("Invalid hero!");
|
||||
|
||||
auto spell = getSpell();
|
||||
|
||||
logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->name, hero->name);
|
||||
|
||||
if(!spell->isAdventureSpell())
|
||||
throw cannotFulfillGoalException(spell->name + " is not an adventure spell.");
|
||||
|
||||
if(!hero->canCastThisSpell(spell))
|
||||
throw cannotFulfillGoalException("Hero can not cast " + spell->name);
|
||||
|
||||
if(hero->mana < hero->getSpellCost(spell))
|
||||
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->name);
|
||||
|
||||
return iAmElementar();
|
||||
}
|
||||
|
||||
void AdventureSpellCast::accept(VCAI * ai)
|
||||
{
|
||||
if(town && spellID == SpellID::TOWN_PORTAL)
|
||||
{
|
||||
ai->selectedObject = town->id;
|
||||
}
|
||||
|
||||
auto wait = cb->waitTillRealize;
|
||||
|
||||
cb->waitTillRealize = true;
|
||||
cb->castSpell(hero.h, spellID, tile);
|
||||
|
||||
if(town && spellID == SpellID::TOWN_PORTAL)
|
||||
{
|
||||
// visit town
|
||||
ai->moveHeroToTile(town->visitablePos(), hero);
|
||||
}
|
||||
|
||||
cb->waitTillRealize = wait;
|
||||
|
||||
throw goalFulfilledException(sptr(*this));
|
||||
}
|
||||
|
||||
std::string AdventureSpellCast::name() const
|
||||
{
|
||||
return "AdventureSpellCast " + spellID.toSpell()->name;
|
||||
}
|
||||
|
||||
std::string AdventureSpellCast::completeMessage() const
|
||||
{
|
||||
return "Spell casted successfully " + spellID.toSpell()->name;
|
||||
}
|
44
AI/Nullkiller/Goals/AdventureSpellCast.h
Normal file
44
AI/Nullkiller/Goals/AdventureSpellCast.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* AdventureSpellCast.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT AdventureSpellCast : public CGoal<AdventureSpellCast>
|
||||
{
|
||||
private:
|
||||
SpellID spellID;
|
||||
|
||||
public:
|
||||
AdventureSpellCast(HeroPtr hero, SpellID spellID)
|
||||
: CGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID)
|
||||
{
|
||||
sethero(hero);
|
||||
}
|
||||
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
|
||||
const CSpell * getSpell() const
|
||||
{
|
||||
return spellID.toSpell();
|
||||
}
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const AdventureSpellCast & other) const override;
|
||||
};
|
||||
}
|
93
AI/Nullkiller/Goals/Build.cpp
Normal file
93
AI/Nullkiller/Goals/Build.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Build.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 "Build.h"
|
||||
#include "BuildThis.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
TGoalVec Build::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec ret;
|
||||
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
//start fresh with every town
|
||||
ai->ah->getBuildingOptions(t);
|
||||
auto immediateBuilding = ai->ah->immediateBuilding();
|
||||
auto expensiveBuilding = ai->ah->expensiveBuilding();
|
||||
|
||||
//handling for early town development to save money and focus on income
|
||||
if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.is_initialized())
|
||||
{
|
||||
auto potentialBuilding = expensiveBuilding.get();
|
||||
switch(expensiveBuilding.get().bid)
|
||||
{
|
||||
case BuildingID::TOWN_HALL:
|
||||
case BuildingID::CITY_HALL:
|
||||
case BuildingID::CAPITOL:
|
||||
case BuildingID::FORT:
|
||||
case BuildingID::CITADEL:
|
||||
case BuildingID::CASTLE:
|
||||
//If above buildings are next to be bought, but no money... do not buy anything else, try to gather resources for these. Simple but has to suffice for now.
|
||||
auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(2.25)));
|
||||
ret.push_back(goal);
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(immediateBuilding.is_initialized())
|
||||
{
|
||||
ret.push_back(sptr(BuildThis(immediateBuilding.get().bid, t).setpriority(2))); //prioritize buildings we can build quick
|
||||
}
|
||||
else //try build later
|
||||
{
|
||||
if(expensiveBuilding.is_initialized())
|
||||
{
|
||||
auto potentialBuilding = expensiveBuilding.get(); //gather resources for any we can't afford
|
||||
auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(0.5)));
|
||||
ret.push_back(goal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ret.empty())
|
||||
throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
TSubgoal Build::whatToDoToAchieve()
|
||||
{
|
||||
return fh->chooseSolution(getAllPossibleSubgoals());
|
||||
}
|
||||
|
||||
bool Build::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if(goal->goalType == BUILD || goal->goalType == BUILD_STRUCTURE)
|
||||
return (!town || town == goal->town); //building anything will do, in this town if set
|
||||
else
|
||||
return false;
|
||||
}
|
37
AI/Nullkiller/Goals/Build.h
Normal file
37
AI/Nullkiller/Goals/Build.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Build.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT Build : public CGoal<Build>
|
||||
{
|
||||
public:
|
||||
Build()
|
||||
: CGoal(Goals::BUILD)
|
||||
{
|
||||
priority = 1;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
|
||||
virtual bool operator==(const Build & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
86
AI/Nullkiller/Goals/BuildBoat.cpp
Normal file
86
AI/Nullkiller/Goals/BuildBoat.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* BuildBoat.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 "BuildBoat.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../lib/CPathfinder.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool BuildBoat::operator==(const BuildBoat & other) const
|
||||
{
|
||||
return shipyard->o->id == other.shipyard->o->id;
|
||||
}
|
||||
|
||||
TSubgoal BuildBoat::whatToDoToAchieve()
|
||||
{
|
||||
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o));
|
||||
}
|
||||
|
||||
if(shipyard->shipyardStatus() != IShipyard::GOOD)
|
||||
{
|
||||
throw cannotFulfillGoalException("Shipyard is busy.");
|
||||
}
|
||||
|
||||
TResources boatCost;
|
||||
shipyard->getBoatCost(boatCost);
|
||||
|
||||
return ai->ah->whatToDo(boatCost, this->iAmElementar());
|
||||
}
|
||||
|
||||
void BuildBoat::accept(VCAI * ai)
|
||||
{
|
||||
TResources boatCost;
|
||||
shipyard->getBoatCost(boatCost);
|
||||
|
||||
if(!cb->getResourceAmount().canAfford(boatCost))
|
||||
{
|
||||
throw cannotFulfillGoalException("Can not afford boat");
|
||||
}
|
||||
|
||||
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
throw cannotFulfillGoalException("Can not build boat in enemy shipyard");
|
||||
}
|
||||
|
||||
if(shipyard->shipyardStatus() != IShipyard::GOOD)
|
||||
{
|
||||
throw cannotFulfillGoalException("Shipyard is busy.");
|
||||
}
|
||||
|
||||
logAi->trace(
|
||||
"Building boat at shipyard %s located at %s, estimated boat position %s",
|
||||
shipyard->o->getObjectName(),
|
||||
shipyard->o->visitablePos().toString(),
|
||||
shipyard->bestLocation().toString());
|
||||
|
||||
cb->buildBoat(shipyard);
|
||||
|
||||
throw goalFulfilledException(sptr(*this));
|
||||
}
|
||||
|
||||
std::string BuildBoat::name() const
|
||||
{
|
||||
return "BuildBoat";
|
||||
}
|
||||
|
||||
std::string BuildBoat::completeMessage() const
|
||||
{
|
||||
return "Boat have been built at " + shipyard->o->visitablePos().toString();
|
||||
}
|
37
AI/Nullkiller/Goals/BuildBoat.h
Normal file
37
AI/Nullkiller/Goals/BuildBoat.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* BuildBoat.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT BuildBoat : public CGoal<BuildBoat>
|
||||
{
|
||||
private:
|
||||
const IShipyard * shipyard;
|
||||
|
||||
public:
|
||||
BuildBoat(const IShipyard * shipyard)
|
||||
: CGoal(Goals::BUILD_BOAT), shipyard(shipyard)
|
||||
{
|
||||
priority = 0;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const BuildBoat & other) const override;
|
||||
};
|
||||
}
|
74
AI/Nullkiller/Goals/BuildThis.cpp
Normal file
74
AI/Nullkiller/Goals/BuildThis.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* BuildThis.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 "BuildThis.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool BuildThis::operator==(const BuildThis & other) const
|
||||
{
|
||||
return town == other.town && bid == other.bid;
|
||||
}
|
||||
|
||||
TSubgoal BuildThis::whatToDoToAchieve()
|
||||
{
|
||||
auto b = BuildingID(bid);
|
||||
|
||||
// find town if not set
|
||||
if(!town && hero)
|
||||
town = hero->visitedTown;
|
||||
|
||||
if(!town)
|
||||
{
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
switch(cb->canBuildStructure(town, b))
|
||||
{
|
||||
case EBuildingState::ALLOWED:
|
||||
town = t;
|
||||
break; //TODO: look for prerequisites? this is not our reponsibility
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(town) //we have specific town to build this
|
||||
{
|
||||
switch(cb->canBuildStructure(town, b))
|
||||
{
|
||||
case EBuildingState::ALLOWED:
|
||||
case EBuildingState::NO_RESOURCES:
|
||||
{
|
||||
auto res = town->town->buildings.at(BuildingID(bid))->resources;
|
||||
return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw cannotFulfillGoalException("Not possible to build");
|
||||
}
|
||||
}
|
||||
else
|
||||
throw cannotFulfillGoalException("Cannot find town to build this");
|
||||
}
|
48
AI/Nullkiller/Goals/BuildThis.h
Normal file
48
AI/Nullkiller/Goals/BuildThis.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* BuildThis.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT BuildThis : public CGoal<BuildThis>
|
||||
{
|
||||
public:
|
||||
BuildThis() //should be private, but unit test uses it
|
||||
: CGoal(Goals::BUILD_STRUCTURE)
|
||||
{
|
||||
}
|
||||
BuildThis(BuildingID Bid, const CGTownInstance * tid)
|
||||
: CGoal(Goals::BUILD_STRUCTURE)
|
||||
{
|
||||
bid = Bid;
|
||||
town = tid;
|
||||
priority = 1;
|
||||
}
|
||||
BuildThis(BuildingID Bid)
|
||||
: CGoal(Goals::BUILD_STRUCTURE)
|
||||
{
|
||||
bid = Bid;
|
||||
priority = 1;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const BuildThis & other) const override;
|
||||
};
|
||||
}
|
45
AI/Nullkiller/Goals/BuyArmy.cpp
Normal file
45
AI/Nullkiller/Goals/BuyArmy.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* BuyArmy.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 "BuyArmy.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool BuyArmy::operator==(const BuyArmy & other) const
|
||||
{
|
||||
return town == other.town && objid == other.objid;
|
||||
}
|
||||
|
||||
bool BuyArmy::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
//if (hero && hero != goal->hero)
|
||||
// return false;
|
||||
return town == goal->town && goal->value >= value; //can always buy more army
|
||||
}
|
||||
TSubgoal BuyArmy::whatToDoToAchieve()
|
||||
{
|
||||
//TODO: calculate the actual cost of units instead
|
||||
TResources price;
|
||||
price[Res::GOLD] = value * 0.4f; //some approximate value
|
||||
return ai->ah->whatToDo(price, iAmElementar()); //buy right now or gather resources
|
||||
}
|
||||
|
||||
std::string BuyArmy::completeMessage() const
|
||||
{
|
||||
return boost::format("Bought army of value %d in town of %s") % value, town->name;
|
||||
}
|
41
AI/Nullkiller/Goals/BuyArmy.h
Normal file
41
AI/Nullkiller/Goals/BuyArmy.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* BuyArmy.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
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 = 3;//TODO: evaluate?
|
||||
}
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const BuyArmy & other) const override;
|
||||
};
|
||||
}
|
89
AI/Nullkiller/Goals/CGoal.h
Normal file
89
AI/Nullkiller/Goals/CGoal.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 "AbstractGoal.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../VCAI.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
template<typename T> class DLL_EXPORT CGoal : public AbstractGoal
|
||||
{
|
||||
public:
|
||||
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
|
||||
{
|
||||
priority = 0;
|
||||
isElementar = false;
|
||||
isAbstract = false;
|
||||
value = 0;
|
||||
aid = -1;
|
||||
objid = -1;
|
||||
resID = -1;
|
||||
tile = int3(-1, -1, -1);
|
||||
town = nullptr;
|
||||
}
|
||||
|
||||
OSETTER(bool, isElementar)
|
||||
OSETTER(bool, isAbstract)
|
||||
OSETTER(float, priority)
|
||||
OSETTER(int, value)
|
||||
OSETTER(int, resID)
|
||||
OSETTER(int, objid)
|
||||
OSETTER(int, aid)
|
||||
OSETTER(int3, tile)
|
||||
OSETTER(HeroPtr, hero)
|
||||
OSETTER(CGTownInstance *, town)
|
||||
OSETTER(int, bid)
|
||||
|
||||
void accept(VCAI * ai) override
|
||||
{
|
||||
ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
|
||||
float accept(FuzzyHelper * f) override
|
||||
{
|
||||
return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
|
||||
CGoal<T> * clone() const override
|
||||
{
|
||||
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
TSubgoal iAmElementar() const
|
||||
{
|
||||
TSubgoal ptr;
|
||||
|
||||
ptr.reset(clone());
|
||||
ptr->setisElementar(true);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & static_cast<AbstractGoal &>(*this);
|
||||
//h & goalType & isElementar & isAbstract & priority;
|
||||
//h & value & resID & objid & aid & tile & hero & town & bid;
|
||||
}
|
||||
|
||||
virtual bool operator==(const AbstractGoal & g) const override
|
||||
{
|
||||
if(goalType != g.goalType)
|
||||
return false;
|
||||
|
||||
return (*this) == (static_cast<const T &>(g));
|
||||
}
|
||||
|
||||
virtual bool operator==(const T & other) const = 0;
|
||||
};
|
||||
}
|
85
AI/Nullkiller/Goals/ClearWayTo.cpp
Normal file
85
AI/Nullkiller/Goals/ClearWayTo.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* ClearWayTo.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 "ClearWayTo.h"
|
||||
#include "Explore.h"
|
||||
#include "RecruitHero.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../AIhelper.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool ClearWayTo::operator==(const ClearWayTo & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.tile == tile;
|
||||
}
|
||||
|
||||
TSubgoal ClearWayTo::whatToDoToAchieve()
|
||||
{
|
||||
assert(cb->isInTheMap(tile)); //set tile
|
||||
if(!cb->isVisible(tile))
|
||||
{
|
||||
logAi->error("Clear way should be used with visible tiles!");
|
||||
return sptr(Explore());
|
||||
}
|
||||
|
||||
return (fh->chooseSolution(getAllPossibleSubgoals()));
|
||||
}
|
||||
|
||||
bool ClearWayTo::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == VISIT_TILE)
|
||||
{
|
||||
if (!hero || hero == goal->hero)
|
||||
return tile == goal->tile;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TGoalVec ClearWayTo::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec ret;
|
||||
|
||||
std::vector<const CGHeroInstance *> heroes;
|
||||
if(hero)
|
||||
heroes.push_back(hero.h);
|
||||
else
|
||||
heroes = cb->getHeroesInfo();
|
||||
|
||||
for(auto h : heroes)
|
||||
{
|
||||
//TODO: handle clearing way to allied heroes that are blocked
|
||||
//if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
|
||||
// h->visitablePos() == tile) //we are already on that tile! what does it mean?
|
||||
// continue;
|
||||
|
||||
//if our hero is trapped, make sure we request clearing the way from OUR perspective
|
||||
|
||||
vstd::concatenate(ret, ai->ah->howToVisitTile(h, tile));
|
||||
}
|
||||
|
||||
if(ret.empty() && ai->canRecruitAnyHero())
|
||||
ret.push_back(sptr(RecruitHero()));
|
||||
|
||||
if(ret.empty())
|
||||
{
|
||||
logAi->warn("There is no known way to clear the way to tile %s", tile.toString());
|
||||
throw goalFulfilledException(sptr(ClearWayTo(tile))); //make sure asigned hero gets unlocked
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
45
AI/Nullkiller/Goals/ClearWayTo.h
Normal file
45
AI/Nullkiller/Goals/ClearWayTo.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* ClearWayTo.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT ClearWayTo : public CGoal<ClearWayTo>
|
||||
{
|
||||
public:
|
||||
ClearWayTo()
|
||||
: CGoal(Goals::CLEAR_WAY_TO)
|
||||
{
|
||||
}
|
||||
ClearWayTo(int3 Tile)
|
||||
: CGoal(Goals::CLEAR_WAY_TO)
|
||||
{
|
||||
tile = Tile;
|
||||
priority = 5;
|
||||
}
|
||||
ClearWayTo(int3 Tile, HeroPtr h)
|
||||
: CGoal(Goals::CLEAR_WAY_TO)
|
||||
{
|
||||
tile = Tile;
|
||||
hero = h;
|
||||
priority = 5;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const ClearWayTo & other) const override;
|
||||
};
|
||||
}
|
208
AI/Nullkiller/Goals/CollectRes.cpp
Normal file
208
AI/Nullkiller/Goals/CollectRes.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* CollectRes.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool CollectRes::operator==(const CollectRes & other) const
|
||||
{
|
||||
return resID == other.resID;
|
||||
}
|
||||
|
||||
TGoalVec CollectRes::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec ret;
|
||||
|
||||
auto givesResource = [this](const CGObjectInstance * obj) -> bool
|
||||
{
|
||||
//TODO: move this logic to object side
|
||||
//TODO: remember mithril exists
|
||||
//TODO: water objects
|
||||
//TODO: Creature banks
|
||||
|
||||
//return false first from once-visitable, before checking if they were even visited
|
||||
switch (obj->ID.num)
|
||||
{
|
||||
case Obj::TREASURE_CHEST:
|
||||
return resID == Res::GOLD;
|
||||
break;
|
||||
case Obj::RESOURCE:
|
||||
return obj->subID == resID;
|
||||
break;
|
||||
case Obj::MINE:
|
||||
return (obj->subID == resID &&
|
||||
(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
|
||||
break;
|
||||
case Obj::CAMPFIRE:
|
||||
return true; //contains all resources
|
||||
break;
|
||||
case Obj::WINDMILL:
|
||||
switch (resID)
|
||||
{
|
||||
case Res::GOLD:
|
||||
case Res::WOOD:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Obj::WATER_WHEEL:
|
||||
if (resID != Res::GOLD)
|
||||
return false;
|
||||
break;
|
||||
case Obj::MYSTICAL_GARDEN:
|
||||
if ((resID != Res::GOLD) && (resID != Res::GEMS))
|
||||
return false;
|
||||
break;
|
||||
case Obj::LEAN_TO:
|
||||
case Obj::WAGON:
|
||||
if (resID != Res::GOLD)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
|
||||
};
|
||||
|
||||
std::vector<const CGObjectInstance *> objs;
|
||||
for (auto obj : ai->visitableObjs)
|
||||
{
|
||||
if (givesResource(obj))
|
||||
objs.push_back(obj);
|
||||
}
|
||||
for (auto h : cb->getHeroesInfo())
|
||||
{
|
||||
std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
|
||||
|
||||
for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
|
||||
{
|
||||
if (givesResource(obj))
|
||||
ourObjs.push_back(obj);
|
||||
}
|
||||
|
||||
for (auto obj : ourObjs)
|
||||
{
|
||||
auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
|
||||
|
||||
vstd::concatenate(ret, waysToGo);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TSubgoal CollectRes::whatToDoToAchieve()
|
||||
{
|
||||
auto goals = getAllPossibleSubgoals();
|
||||
auto trade = whatToDoToTrade();
|
||||
if (!trade->invalid())
|
||||
goals.push_back(trade);
|
||||
|
||||
if (goals.empty())
|
||||
return sptr(Explore()); //we can always do that
|
||||
else
|
||||
return fh->chooseSolution(goals); //TODO: evaluate trading
|
||||
}
|
||||
|
||||
TSubgoal CollectRes::whatToDoToTrade()
|
||||
{
|
||||
std::vector<const IMarket *> markets;
|
||||
|
||||
std::vector<const CGObjectInstance *> visObjs;
|
||||
ai->retrieveVisitableObjs(visObjs, true);
|
||||
for (const CGObjectInstance * obj : visObjs)
|
||||
{
|
||||
if (const IMarket * m = IMarket::castFrom(obj, false))
|
||||
{
|
||||
if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
|
||||
markets.push_back(m);
|
||||
else if (obj->ID == Obj::TRADING_POST)
|
||||
markets.push_back(m);
|
||||
}
|
||||
}
|
||||
|
||||
boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
|
||||
{
|
||||
return m1->getMarketEfficiency() < m2->getMarketEfficiency();
|
||||
});
|
||||
|
||||
markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
|
||||
{
|
||||
if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
|
||||
{
|
||||
if (!ai->isAccessible(market->o->visitablePos()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}), markets.end());
|
||||
|
||||
if (!markets.size())
|
||||
{
|
||||
for (const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
|
||||
return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const IMarket * m = markets.back();
|
||||
//attempt trade at back (best prices)
|
||||
int howManyCanWeBuy = 0;
|
||||
for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
|
||||
{
|
||||
if (i == resID)
|
||||
continue;
|
||||
int toGive = -1, toReceive = -1;
|
||||
m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
|
||||
assert(toGive > 0 && toReceive > 0);
|
||||
howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
|
||||
}
|
||||
|
||||
if (howManyCanWeBuy >= value)
|
||||
{
|
||||
auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
||||
assert(backObj);
|
||||
auto objid = m->o->id.getNum();
|
||||
if (backObj->tempOwner != ai->playerID) //top object not owned
|
||||
{
|
||||
return sptr(VisitObj(objid)); //just go there
|
||||
}
|
||||
else //either it's our town, or we have hero there
|
||||
{
|
||||
return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately
|
||||
}
|
||||
}
|
||||
}
|
||||
return sptr(Invalid()); //cannot trade
|
||||
}
|
||||
|
||||
bool CollectRes::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->resID == resID)
|
||||
if (goal->value >= value)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
40
AI/Nullkiller/Goals/CollectRes.h
Normal file
40
AI/Nullkiller/Goals/CollectRes.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* CollectRes.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT CollectRes : public CGoal<CollectRes>
|
||||
{
|
||||
public:
|
||||
CollectRes()
|
||||
: CGoal(Goals::COLLECT_RES)
|
||||
{
|
||||
}
|
||||
CollectRes(int rid, int val)
|
||||
: CGoal(Goals::COLLECT_RES)
|
||||
{
|
||||
resID = rid;
|
||||
value = val;
|
||||
priority = 2;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
TSubgoal whatToDoToTrade();
|
||||
bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
|
||||
virtual bool operator==(const CollectRes & other) const override;
|
||||
};
|
||||
}
|
276
AI/Nullkiller/Goals/CompleteQuest.cpp
Normal file
276
AI/Nullkiller/Goals/CompleteQuest.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* CompleteQuest.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../lib/CPathfinder.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool CompleteQuest::operator==(const CompleteQuest & other) const
|
||||
{
|
||||
return q.quest->qid == other.q.quest->qid;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec solutions;
|
||||
|
||||
if(q.quest->missionType && q.quest->progress != CQuest::COMPLETE)
|
||||
{
|
||||
logAi->debug("Trying to realize quest: %s", questToString());
|
||||
|
||||
switch(q.quest->missionType)
|
||||
{
|
||||
case CQuest::MISSION_ART:
|
||||
return missionArt();
|
||||
|
||||
case CQuest::MISSION_HERO:
|
||||
return missionHero();
|
||||
|
||||
case CQuest::MISSION_ARMY:
|
||||
return missionArmy();
|
||||
|
||||
case CQuest::MISSION_RESOURCES:
|
||||
return missionResources();
|
||||
|
||||
case CQuest::MISSION_KILL_HERO:
|
||||
case CQuest::MISSION_KILL_CREATURE:
|
||||
return missionDestroyObj();
|
||||
|
||||
case CQuest::MISSION_PRIMARY_STAT:
|
||||
return missionIncreasePrimaryStat();
|
||||
|
||||
case CQuest::MISSION_LEVEL:
|
||||
return missionLevel();
|
||||
|
||||
case CQuest::MISSION_PLAYER:
|
||||
if(ai->playerID.getNum() != q.quest->m13489val)
|
||||
logAi->debug("Can't be player of color %d", q.quest->m13489val);
|
||||
|
||||
break;
|
||||
|
||||
case CQuest::MISSION_KEYMASTER:
|
||||
return missionKeymaster();
|
||||
|
||||
} //end of switch
|
||||
}
|
||||
|
||||
return TGoalVec();
|
||||
}
|
||||
|
||||
TSubgoal CompleteQuest::whatToDoToAchieve()
|
||||
{
|
||||
if(q.quest->missionType == CQuest::MISSION_NONE)
|
||||
{
|
||||
throw cannotFulfillGoalException("Can not complete inactive quest");
|
||||
}
|
||||
|
||||
TGoalVec solutions = getAllPossibleSubgoals();
|
||||
|
||||
if(solutions.empty())
|
||||
throw cannotFulfillGoalException("Can not complete quest " + questToString());
|
||||
|
||||
TSubgoal result = fh->chooseSolution(solutions);
|
||||
|
||||
logAi->trace(
|
||||
"Returning %s, tile: %s, objid: %d, hero: %s",
|
||||
result->name(),
|
||||
result->tile.toString(),
|
||||
result->objid,
|
||||
result->hero.validAndSet() ? result->hero->name : "not specified");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string CompleteQuest::name() const
|
||||
{
|
||||
return "CompleteQuest";
|
||||
}
|
||||
|
||||
std::string CompleteQuest::completeMessage() const
|
||||
{
|
||||
return "Completed quest " + questToString();
|
||||
}
|
||||
|
||||
std::string CompleteQuest::questToString() const
|
||||
{
|
||||
if(q.quest->missionType == CQuest::MISSION_NONE)
|
||||
return "inactive quest";
|
||||
|
||||
MetaString ms;
|
||||
q.quest->getRolloverText(ms, false);
|
||||
|
||||
return ms.toString();
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::tryCompleteQuest() const
|
||||
{
|
||||
TGoalVec solutions;
|
||||
|
||||
auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
if(q.quest->checkQuest(hero))
|
||||
{
|
||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id)));
|
||||
}
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionArt() const
|
||||
{
|
||||
TGoalVec solutions = tryCompleteQuest();
|
||||
|
||||
if(!solutions.empty())
|
||||
return solutions;
|
||||
|
||||
for(auto art : q.quest->m5arts)
|
||||
{
|
||||
solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport?
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionHero() const
|
||||
{
|
||||
TGoalVec solutions = tryCompleteQuest();
|
||||
|
||||
if(solutions.empty())
|
||||
{
|
||||
//rule of a thumb - quest heroes usually are locked in prisons
|
||||
solutions.push_back(sptr(FindObj(Obj::PRISON)));
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionArmy() const
|
||||
{
|
||||
TGoalVec solutions = tryCompleteQuest();
|
||||
|
||||
if(!solutions.empty())
|
||||
return solutions;
|
||||
|
||||
for(auto creature : q.quest->m6creatures)
|
||||
{
|
||||
solutions.push_back(sptr(GatherTroops(creature.type->idNumber, creature.count)));
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionIncreasePrimaryStat() const
|
||||
{
|
||||
TGoalVec solutions = tryCompleteQuest();
|
||||
|
||||
if(solutions.empty())
|
||||
{
|
||||
for(int i = 0; i < q.quest->m2stats.size(); ++i)
|
||||
{
|
||||
// TODO: library, school and other boost objects
|
||||
logAi->debug("Don't know how to increase primary stat %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionLevel() const
|
||||
{
|
||||
TGoalVec solutions = tryCompleteQuest();
|
||||
|
||||
if(solutions.empty())
|
||||
{
|
||||
logAi->debug("Don't know how to reach hero level %d", q.quest->m13489val);
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionKeymaster() const
|
||||
{
|
||||
TGoalVec solutions = tryCompleteQuest();
|
||||
|
||||
if(solutions.empty())
|
||||
{
|
||||
solutions.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, q.obj->subID)));
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionResources() const
|
||||
{
|
||||
TGoalVec solutions;
|
||||
|
||||
auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
|
||||
|
||||
if(heroes.size())
|
||||
{
|
||||
if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is
|
||||
{
|
||||
return ai->ah->howToVisitObj(q.obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < q.quest->m7resources.size(); ++i)
|
||||
{
|
||||
if(q.quest->m7resources[i])
|
||||
solutions.push_back(sptr(CollectRes(i, q.quest->m7resources[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :(
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::missionDestroyObj() const
|
||||
{
|
||||
TGoalVec solutions;
|
||||
|
||||
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
|
||||
|
||||
if(!obj)
|
||||
return ai->ah->howToVisitObj(q.obj);
|
||||
|
||||
if(obj->ID == Obj::HERO)
|
||||
{
|
||||
auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);
|
||||
|
||||
if(relations == PlayerRelations::SAME_PLAYER)
|
||||
{
|
||||
auto heroToProtect = cb->getHero(obj->id);
|
||||
|
||||
solutions.push_back(sptr(GatherArmy().sethero(heroToProtect)));
|
||||
}
|
||||
else if(relations == PlayerRelations::ENEMIES)
|
||||
{
|
||||
solutions = ai->ah->howToVisitObj(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
46
AI/Nullkiller/Goals/CompleteQuest.h
Normal file
46
AI/Nullkiller/Goals/CompleteQuest.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* CompleteQuest.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
#include "../../../lib/VCMI_Lib.h"
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT CompleteQuest : public CGoal<CompleteQuest>
|
||||
{
|
||||
private:
|
||||
const QuestInfo q;
|
||||
|
||||
public:
|
||||
CompleteQuest(const QuestInfo quest)
|
||||
: CGoal(Goals::COMPLETE_QUEST), q(quest)
|
||||
{
|
||||
}
|
||||
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const CompleteQuest & other) const override;
|
||||
|
||||
private:
|
||||
TGoalVec tryCompleteQuest() const;
|
||||
TGoalVec missionArt() const;
|
||||
TGoalVec missionHero() const;
|
||||
TGoalVec missionArmy() const;
|
||||
TGoalVec missionResources() const;
|
||||
TGoalVec missionDestroyObj() const;
|
||||
TGoalVec missionIncreasePrimaryStat() const;
|
||||
TGoalVec missionLevel() const;
|
||||
TGoalVec missionKeymaster() const;
|
||||
std::string questToString() const;
|
||||
};
|
||||
}
|
90
AI/Nullkiller/Goals/Conquer.cpp
Normal file
90
AI/Nullkiller/Goals/Conquer.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Conquer.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool Conquer::operator==(const Conquer & other) const
|
||||
{
|
||||
return other.hero.h == hero.h;
|
||||
}
|
||||
|
||||
TSubgoal Conquer::whatToDoToAchieve()
|
||||
{
|
||||
logAi->trace("Entering goal CONQUER");
|
||||
|
||||
return fh->chooseSolution(getAllPossibleSubgoals());
|
||||
}
|
||||
|
||||
TGoalVec Conquer::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec ret;
|
||||
|
||||
auto conquerable = [](const CGObjectInstance * obj) -> bool
|
||||
{
|
||||
if(cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
switch(obj->ID.num)
|
||||
{
|
||||
case Obj::TOWN:
|
||||
case Obj::HERO:
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
case Obj::MINE: //TODO: check ai->knownSubterraneanGates
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
std::vector<const CGObjectInstance *> objs;
|
||||
for(auto obj : ai->visitableObjs)
|
||||
{
|
||||
if(conquerable(obj))
|
||||
objs.push_back(obj);
|
||||
}
|
||||
|
||||
for(auto h : cb->getHeroesInfo())
|
||||
{
|
||||
std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
|
||||
|
||||
for(auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
|
||||
{
|
||||
if(conquerable(obj))
|
||||
ourObjs.push_back(obj);
|
||||
}
|
||||
for(auto obj : ourObjs)
|
||||
{
|
||||
auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
|
||||
|
||||
vstd::concatenate(ret, waysToGo);
|
||||
}
|
||||
}
|
||||
if(!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
|
||||
ret.push_back(sptr(RecruitHero()));
|
||||
|
||||
if(ret.empty())
|
||||
ret.push_back(sptr(Explore())); //we need to find an enemy
|
||||
return ret;
|
||||
}
|
32
AI/Nullkiller/Goals/Conquer.h
Normal file
32
AI/Nullkiller/Goals/Conquer.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Conquer.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT Conquer : public CGoal<Conquer>
|
||||
{
|
||||
public:
|
||||
Conquer()
|
||||
: CGoal(Goals::CONQUER)
|
||||
{
|
||||
priority = 10;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const Conquer & other) const override;
|
||||
};
|
||||
}
|
39
AI/Nullkiller/Goals/DigAtTile.cpp
Normal file
39
AI/Nullkiller/Goals/DigAtTile.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* DigAtTile.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 "DigAtTile.h"
|
||||
#include "VisitTile.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool DigAtTile::operator==(const DigAtTile & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.tile == tile;
|
||||
}
|
||||
|
||||
TSubgoal DigAtTile::whatToDoToAchieve()
|
||||
{
|
||||
const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
|
||||
if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
|
||||
{
|
||||
const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj);
|
||||
sethero(h).setisElementar(true);
|
||||
return sptr(*this);
|
||||
}
|
||||
|
||||
return sptr(VisitTile(tile));
|
||||
}
|
41
AI/Nullkiller/Goals/DigAtTile.h
Normal file
41
AI/Nullkiller/Goals/DigAtTile.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* DigAtTile.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT DigAtTile : public CGoal<DigAtTile>
|
||||
//elementar with hero on tile
|
||||
{
|
||||
public:
|
||||
DigAtTile()
|
||||
: CGoal(Goals::DIG_AT_TILE)
|
||||
{
|
||||
}
|
||||
DigAtTile(int3 Tile)
|
||||
: CGoal(Goals::DIG_AT_TILE)
|
||||
{
|
||||
tile = Tile;
|
||||
priority = 20;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const DigAtTile & other) const override;
|
||||
};
|
||||
}
|
449
AI/Nullkiller/Goals/Explore.cpp
Normal file
449
AI/Nullkiller/Goals/Explore.cpp
Normal file
@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Explore.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
struct ExplorationHelper
|
||||
{
|
||||
HeroPtr hero;
|
||||
int sightRadius;
|
||||
float bestValue;
|
||||
TSubgoal bestGoal;
|
||||
VCAI * aip;
|
||||
CCallback * cbp;
|
||||
const TeamState * ts;
|
||||
int3 ourPos;
|
||||
bool allowDeadEndCancellation;
|
||||
bool allowGatherArmy;
|
||||
|
||||
ExplorationHelper(HeroPtr h, bool gatherArmy)
|
||||
{
|
||||
cbp = cb.get();
|
||||
aip = ai.get();
|
||||
hero = h;
|
||||
ts = cbp->getPlayerTeam(ai->playerID);
|
||||
sightRadius = hero->getSightRadius();
|
||||
bestGoal = sptr(Goals::Invalid());
|
||||
bestValue = 0;
|
||||
ourPos = h->convertPosition(h->pos, false);
|
||||
allowDeadEndCancellation = true;
|
||||
allowGatherArmy = gatherArmy;
|
||||
}
|
||||
|
||||
void scanSector(int scanRadius)
|
||||
{
|
||||
for(int x = ourPos.x - scanRadius; x <= ourPos.x + scanRadius; x++)
|
||||
{
|
||||
for(int y = ourPos.y - scanRadius; y <= ourPos.y + scanRadius; y++)
|
||||
{
|
||||
int3 tile = int3(x, y, ourPos.z);
|
||||
|
||||
if(cbp->isInTheMap(tile) && ts->fogOfWarMap[tile.x][tile.y][tile.z])
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scanMap()
|
||||
{
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||
|
||||
std::vector<int3> from;
|
||||
std::vector<int3> to;
|
||||
|
||||
from.reserve(perimeter);
|
||||
to.reserve(perimeter);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
if(ts->fogOfWarMap[pos.x][pos.y][pos.z])
|
||||
{
|
||||
bool hasInvisibleNeighbor = false;
|
||||
|
||||
foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(!ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z])
|
||||
{
|
||||
hasInvisibleNeighbor = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(hasInvisibleNeighbor)
|
||||
from.push_back(pos);
|
||||
}
|
||||
});
|
||||
|
||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero.name);
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
if(!bestGoal->invalid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
allowDeadEndCancellation = false;
|
||||
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
getVisibleNeighbours(from, to);
|
||||
vstd::concatenate(from, to);
|
||||
vstd::removeDuplicates(from);
|
||||
}
|
||||
|
||||
logAi->debug("Exploration scan all possible tiles for hero %s", hero.name);
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
void scanTile(const int3 & tile)
|
||||
{
|
||||
if(tile == ourPos
|
||||
|| !aip->ah->isTileAccessible(hero, tile)) //shouldn't happen, but it does
|
||||
return;
|
||||
|
||||
int tilesDiscovered = howManyTilesWillBeDiscovered(tile);
|
||||
if(!tilesDiscovered)
|
||||
return;
|
||||
|
||||
auto waysToVisit = aip->ah->howToVisitTile(hero, tile, allowGatherArmy);
|
||||
for(auto goal : waysToVisit)
|
||||
{
|
||||
if(goal->evaluationContext.movementCost <= 0.0) // should not happen
|
||||
continue;
|
||||
|
||||
float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost;
|
||||
|
||||
if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
||||
{
|
||||
auto obj = cb->getTopObj(tile);
|
||||
|
||||
// picking up resources does not yield any exploration at all.
|
||||
// if it blocks the way to some explorable tile AIPathfinder will take care of it
|
||||
if(obj && obj->blockVisit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isSafeToVisit(hero, tile))
|
||||
{
|
||||
bestGoal = goal;
|
||||
bestValue = ourValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
|
||||
{
|
||||
for(const int3 & tile : tiles)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z])
|
||||
{
|
||||
out.push_back(neighbour);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int howManyTilesWillBeDiscovered(
|
||||
const int3 & pos) const
|
||||
{
|
||||
int ret = 0;
|
||||
for(int x = pos.x - sightRadius; x <= pos.x + sightRadius; x++)
|
||||
{
|
||||
for(int y = pos.y - sightRadius; y <= pos.y + sightRadius; y++)
|
||||
{
|
||||
int3 npos = int3(x, y, pos.z);
|
||||
if(cbp->isInTheMap(npos)
|
||||
&& pos.dist2d(npos) - 0.5 < sightRadius
|
||||
&& !ts->fogOfWarMap[npos.x][npos.y][npos.z])
|
||||
{
|
||||
if(allowDeadEndCancellation
|
||||
&& !hasReachableNeighbor(npos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool hasReachableNeighbor(const int3 &pos) const
|
||||
{
|
||||
for(crint3 dir : int3::getDirs())
|
||||
{
|
||||
int3 tile = pos + dir;
|
||||
if(cbp->isInTheMap(tile))
|
||||
{
|
||||
auto isAccessible = aip->ah->isTileAccessible(hero, tile);
|
||||
|
||||
if(isAccessible)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool Explore::operator==(const Explore & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy;
|
||||
}
|
||||
|
||||
std::string Explore::completeMessage() const
|
||||
{
|
||||
return "Hero " + hero.get()->name + " completed exploration";
|
||||
}
|
||||
|
||||
TSubgoal Explore::whatToDoToAchieve()
|
||||
{
|
||||
return fh->chooseSolution(getAllPossibleSubgoals());
|
||||
}
|
||||
|
||||
TGoalVec Explore::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec ret;
|
||||
std::vector<const CGHeroInstance *> heroes;
|
||||
|
||||
if(hero)
|
||||
{
|
||||
heroes.push_back(hero.h);
|
||||
}
|
||||
else
|
||||
{
|
||||
//heroes = ai->getUnblockedHeroes();
|
||||
heroes = cb->getHeroesInfo();
|
||||
vstd::erase_if(heroes, [](const HeroPtr h)
|
||||
{
|
||||
if(ai->getGoal(h)->goalType == EXPLORE) //do not reassign hero who is already explorer
|
||||
return true;
|
||||
|
||||
if(!ai->isAbleToExplore(h))
|
||||
return true;
|
||||
|
||||
return !h->movement; //saves time, immobile heroes are useless anyway
|
||||
});
|
||||
}
|
||||
|
||||
//try to use buildings that uncover map
|
||||
std::vector<const CGObjectInstance *> objs;
|
||||
for(auto obj : ai->visitableObjs)
|
||||
{
|
||||
if(!vstd::contains(ai->alreadyVisited, obj))
|
||||
{
|
||||
switch(obj->ID.num)
|
||||
{
|
||||
case Obj::REDWOOD_OBSERVATORY:
|
||||
case Obj::PILLAR_OF_FIRE:
|
||||
case Obj::CARTOGRAPHER:
|
||||
objs.push_back(obj);
|
||||
break;
|
||||
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
|
||||
case Obj::MONOLITH_TWO_WAY:
|
||||
case Obj::SUBTERRANEAN_GATE:
|
||||
auto tObj = dynamic_cast<const CGTeleport *>(obj);
|
||||
assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end());
|
||||
if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability)
|
||||
objs.push_back(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(obj->ID.num)
|
||||
{
|
||||
case Obj::MONOLITH_TWO_WAY:
|
||||
case Obj::SUBTERRANEAN_GATE:
|
||||
auto tObj = dynamic_cast<const CGTeleport *>(obj);
|
||||
if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability)
|
||||
break;
|
||||
for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits)
|
||||
{
|
||||
if(!cb->getObj(exit))
|
||||
{ // Always attempt to visit two-way teleports if one of channel exits is not visible
|
||||
objs.push_back(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto h : heroes)
|
||||
{
|
||||
for(auto obj : objs) //double loop, performance risk?
|
||||
{
|
||||
auto waysToVisitObj = ai->ah->howToVisitObj(h, obj, allowGatherArmy);
|
||||
|
||||
vstd::concatenate(ret, waysToVisitObj);
|
||||
}
|
||||
|
||||
TSubgoal goal = exploreNearestNeighbour(h);
|
||||
|
||||
if(!goal->invalid())
|
||||
{
|
||||
ret.push_back(goal);
|
||||
}
|
||||
}
|
||||
|
||||
if(ret.empty())
|
||||
{
|
||||
for(auto h : heroes)
|
||||
{
|
||||
logAi->trace("Exploration searching for a new point for hero %s", h->name);
|
||||
|
||||
TSubgoal goal = explorationNewPoint(h);
|
||||
|
||||
if(goal->invalid())
|
||||
{
|
||||
ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back(goal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//we either don't have hero yet or none of heroes can explore
|
||||
if((!hero || ret.empty()) && ai->canRecruitAnyHero())
|
||||
ret.push_back(sptr(RecruitHero()));
|
||||
|
||||
if(ret.empty())
|
||||
{
|
||||
throw goalFulfilledException(sptr(Explore().sethero(hero)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Explore::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if(goal->goalType == EXPLORE)
|
||||
{
|
||||
if(goal->hero)
|
||||
return hero == goal->hero;
|
||||
else
|
||||
return true; //cancel ALL exploration
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const
|
||||
{
|
||||
ExplorationHelper scanResult(h, allowGatherArmy);
|
||||
|
||||
for(crint3 dir : int3::getDirs())
|
||||
{
|
||||
int3 tile = hpos + dir;
|
||||
if(cb->isInTheMap(tile))
|
||||
{
|
||||
scanResult.scanTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
return scanResult.bestGoal;
|
||||
}
|
||||
|
||||
|
||||
TSubgoal Explore::explorationNewPoint(HeroPtr h) const
|
||||
{
|
||||
ExplorationHelper scanResult(h, allowGatherArmy);
|
||||
|
||||
scanResult.scanSector(10);
|
||||
|
||||
if(!scanResult.bestGoal->invalid())
|
||||
{
|
||||
return scanResult.bestGoal;
|
||||
}
|
||||
|
||||
scanResult.scanMap();
|
||||
|
||||
return scanResult.bestGoal;
|
||||
}
|
||||
|
||||
|
||||
TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const
|
||||
{
|
||||
TimeCheck tc("where to explore");
|
||||
int3 hpos = h->visitablePos();
|
||||
|
||||
//look for nearby objs -> visit them if they're close enough
|
||||
const int DIST_LIMIT = 3;
|
||||
const float COST_LIMIT = .2; //todo: fine tune
|
||||
|
||||
std::vector<const CGObjectInstance *> nearbyVisitableObjs;
|
||||
for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map
|
||||
{
|
||||
for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y)
|
||||
{
|
||||
for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false))
|
||||
{
|
||||
if(ai->isGoodForVisit(obj, h, COST_LIMIT))
|
||||
{
|
||||
nearbyVisitableObjs.push_back(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(nearbyVisitableObjs.size())
|
||||
{
|
||||
vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
|
||||
boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
|
||||
|
||||
TSubgoal pickupNearestObj = fh->chooseSolution(ai->ah->howToVisitObj(h, nearbyVisitableObjs.back(), false));
|
||||
|
||||
if(!pickupNearestObj->invalid())
|
||||
{
|
||||
return pickupNearestObj;
|
||||
}
|
||||
}
|
||||
|
||||
//check if nearby tiles allow us to reveal anything - this is quick
|
||||
return explorationBestNeighbour(hpos, h);
|
||||
}
|
66
AI/Nullkiller/Goals/Explore.h
Normal file
66
AI/Nullkiller/Goals/Explore.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Explore.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
struct ExplorationHelper;
|
||||
|
||||
class DLL_EXPORT Explore : public CGoal<Explore>
|
||||
{
|
||||
private:
|
||||
bool allowGatherArmy;
|
||||
|
||||
public:
|
||||
Explore(bool allowGatherArmy)
|
||||
: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
|
||||
{
|
||||
priority = 1;
|
||||
}
|
||||
|
||||
Explore()
|
||||
: Explore(true)
|
||||
{
|
||||
}
|
||||
|
||||
Explore(HeroPtr h)
|
||||
: CGoal(Goals::EXPLORE)
|
||||
{
|
||||
hero = h;
|
||||
priority = 1;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const Explore & other) const override;
|
||||
|
||||
private:
|
||||
TSubgoal exploreNearestNeighbour(HeroPtr h) const;
|
||||
TSubgoal explorationNewPoint(HeroPtr h) const;
|
||||
TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const;
|
||||
void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const;
|
||||
bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const;
|
||||
|
||||
void getVisibleNeighbours(
|
||||
const std::vector<int3> & tiles,
|
||||
std::vector<int3> & out,
|
||||
CCallback * cbp,
|
||||
const TeamState * ts) const;
|
||||
|
||||
int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const;
|
||||
};
|
||||
}
|
70
AI/Nullkiller/Goals/FindObj.cpp
Normal file
70
AI/Nullkiller/Goals/FindObj.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* FindObj.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 "FindObj.h"
|
||||
#include "VisitObj.h"
|
||||
#include "Explore.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool FindObj::operator==(const FindObj & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.objid == objid;
|
||||
}
|
||||
|
||||
TSubgoal FindObj::whatToDoToAchieve()
|
||||
{
|
||||
const CGObjectInstance * o = nullptr;
|
||||
if(resID > -1) //specified
|
||||
{
|
||||
for(const CGObjectInstance * obj : ai->visitableObjs)
|
||||
{
|
||||
if(obj->ID == objid && obj->subID == resID)
|
||||
{
|
||||
o = obj;
|
||||
break; //TODO: consider multiple objects and choose best
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(const CGObjectInstance * obj : ai->visitableObjs)
|
||||
{
|
||||
if(obj->ID == objid)
|
||||
{
|
||||
o = obj;
|
||||
break; //TODO: consider multiple objects and choose best
|
||||
}
|
||||
}
|
||||
}
|
||||
if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
|
||||
return sptr(VisitObj(o->id.getNum()));
|
||||
else
|
||||
return sptr(Explore());
|
||||
}
|
||||
|
||||
bool FindObj::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == VISIT_TILE) //visiting tile visits object at same time
|
||||
{
|
||||
if (!hero || hero == goal->hero)
|
||||
for (auto obj : cb->getVisitableObjs(goal->tile)) //check if any object on that tile matches criteria
|
||||
if (obj->visitablePos() == goal->tile) //object could be removed
|
||||
if (obj->ID == objid && obj->subID == resID) //same type and subtype
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
47
AI/Nullkiller/Goals/FindObj.h
Normal file
47
AI/Nullkiller/Goals/FindObj.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* FindObj.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT FindObj : public CGoal<FindObj>
|
||||
{
|
||||
public:
|
||||
FindObj() {} // empty constructor not allowed
|
||||
|
||||
FindObj(int ID)
|
||||
: CGoal(Goals::FIND_OBJ)
|
||||
{
|
||||
objid = ID;
|
||||
resID = -1; //subid unspecified
|
||||
priority = 1;
|
||||
}
|
||||
FindObj(int ID, int subID)
|
||||
: CGoal(Goals::FIND_OBJ)
|
||||
{
|
||||
objid = ID;
|
||||
resID = subID;
|
||||
priority = 1;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const FindObj & other) const override;
|
||||
};
|
||||
}
|
210
AI/Nullkiller/Goals/GatherArmy.cpp
Normal file
210
AI/Nullkiller/Goals/GatherArmy.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* GatherArmy.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool GatherArmy::operator==(const GatherArmy & other) const
|
||||
{
|
||||
return other.hero.h == hero.h || town == other.town;
|
||||
}
|
||||
|
||||
std::string GatherArmy::completeMessage() const
|
||||
{
|
||||
return "Hero " + hero.get()->name + " gathered army of value " + boost::lexical_cast<std::string>(value);
|
||||
}
|
||||
|
||||
TSubgoal GatherArmy::whatToDoToAchieve()
|
||||
{
|
||||
//TODO: find hero if none set
|
||||
assert(hero.h);
|
||||
|
||||
return fh->chooseSolution(getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
|
||||
}
|
||||
|
||||
TGoalVec GatherArmy::getAllPossibleSubgoals()
|
||||
{
|
||||
//get all possible towns, heroes and dwellings we may use
|
||||
TGoalVec ret;
|
||||
|
||||
if(!hero.validAndSet())
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
//TODO: include evaluation of monsters gather in calculation
|
||||
for(auto t : cb->getTownsInfo())
|
||||
{
|
||||
auto waysToVisit = ai->ah->howToVisitObj(hero, t);
|
||||
|
||||
if(waysToVisit.size())
|
||||
{
|
||||
//grab army from town
|
||||
if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t))
|
||||
{
|
||||
if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
|
||||
vstd::concatenate(ret, waysToVisit);
|
||||
}
|
||||
|
||||
//buy army in town
|
||||
if (!t->visitingHero || t->visitingHero == hero.get(true))
|
||||
{
|
||||
std::vector<int> values = {
|
||||
value,
|
||||
(int)howManyReinforcementsCanBuy(t->getUpperArmy(), t),
|
||||
(int)howManyReinforcementsCanBuy(hero.get(), t) };
|
||||
|
||||
int val = *std::min_element(values.begin(), values.end());
|
||||
|
||||
if (val)
|
||||
{
|
||||
auto goal = sptr(BuyArmy(t, val).sethero(hero));
|
||||
|
||||
if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
|
||||
ret.push_back(goal);
|
||||
else
|
||||
logAi->debug("Can not buy army, because of ai->ah->containsObjective");
|
||||
}
|
||||
}
|
||||
//build dwelling
|
||||
//TODO: plan building over multiple turns?
|
||||
//auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
|
||||
|
||||
//Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
|
||||
/*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
|
||||
if (bid.is_initialized())
|
||||
{
|
||||
auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
|
||||
if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
|
||||
ret.push_back(goal);
|
||||
else
|
||||
logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
auto otherHeroes = cb->getHeroesInfo();
|
||||
auto heroDummy = hero;
|
||||
vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
|
||||
{
|
||||
if(h == heroDummy.h)
|
||||
return true;
|
||||
else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
|
||||
return true;
|
||||
else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue
|
||||
return true;
|
||||
else if(ai->getGoal(h)->goalType == GATHER_ARMY)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
for(auto h : otherHeroes)
|
||||
{
|
||||
// Go to the other hero if we are faster
|
||||
if(!vstd::contains(ai->visitedHeroes[hero], h))
|
||||
{
|
||||
vstd::concatenate(ret, ai->ah->howToVisitObj(hero, h, false));
|
||||
}
|
||||
|
||||
// Go to the other hero if we are faster
|
||||
if(!vstd::contains(ai->visitedHeroes[h], hero))
|
||||
{
|
||||
vstd::concatenate(ret, ai->ah->howToVisitObj(h, hero.get(), false));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const CGObjectInstance *> objs;
|
||||
for(auto obj : ai->visitableObjs)
|
||||
{
|
||||
if(obj->ID == Obj::CREATURE_GENERATOR1)
|
||||
{
|
||||
auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);
|
||||
|
||||
//Use flagged dwellings only when there are available creatures that we can afford
|
||||
if(relationToOwner == PlayerRelations::SAME_PLAYER)
|
||||
{
|
||||
auto dwelling = dynamic_cast<const CGDwelling *>(obj);
|
||||
|
||||
ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero.get(), dwelling));
|
||||
|
||||
if(val)
|
||||
{
|
||||
for(auto & creLevel : dwelling->creatures)
|
||||
{
|
||||
if(creLevel.first)
|
||||
{
|
||||
for(auto & creatureID : creLevel.second)
|
||||
{
|
||||
auto creature = VLC->creh->creatures[creatureID];
|
||||
if(ai->ah->freeResources().canAfford(creature->cost))
|
||||
objs.push_back(obj); //TODO: reserve resources?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto h : cb->getHeroesInfo())
|
||||
{
|
||||
for(auto obj : objs)
|
||||
{
|
||||
//find safe dwelling
|
||||
if(ai->isGoodForVisit(obj, h))
|
||||
{
|
||||
vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
|
||||
{
|
||||
if(auto t = ai->findTownWithTavern())
|
||||
{
|
||||
for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes
|
||||
{
|
||||
if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy
|
||||
{
|
||||
ret.push_back(sptr(RecruitHero()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ret.empty())
|
||||
{
|
||||
const bool allowGatherArmy = false;
|
||||
|
||||
if(hero == ai->primaryHero())
|
||||
ret.push_back(sptr(Explore(allowGatherArmy)));
|
||||
else
|
||||
throw cannotFulfillGoalException("No ways to gather army");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
38
AI/Nullkiller/Goals/GatherArmy.h
Normal file
38
AI/Nullkiller/Goals/GatherArmy.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* GatherArmy.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT GatherArmy : public CGoal<GatherArmy>
|
||||
{
|
||||
public:
|
||||
GatherArmy()
|
||||
: CGoal(Goals::GATHER_ARMY)
|
||||
{
|
||||
}
|
||||
GatherArmy(int val)
|
||||
: CGoal(Goals::GATHER_ARMY)
|
||||
{
|
||||
value = val;
|
||||
priority = 2.5;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const GatherArmy & other) const override;
|
||||
};
|
||||
}
|
149
AI/Nullkiller/Goals/GatherTroops.cpp
Normal file
149
AI/Nullkiller/Goals/GatherTroops.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* GatherTroops.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool GatherTroops::operator==(const GatherTroops & other) const
|
||||
{
|
||||
return objid == other.objid;
|
||||
}
|
||||
|
||||
int GatherTroops::getCreaturesCount(const CArmedInstance * army)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(auto stack : army->Slots())
|
||||
{
|
||||
if(objid == stack.second->getCreatureID().num)
|
||||
{
|
||||
count += stack.second->count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
{
|
||||
logAi->trace("Entering GatherTroops::whatToDoToAchieve");
|
||||
|
||||
auto heroes = cb->getHeroesInfo(true);
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
if(getCreaturesCount(hero) >= this->value)
|
||||
{
|
||||
logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name);
|
||||
|
||||
throw goalFulfilledException(sptr(*this));
|
||||
}
|
||||
}
|
||||
|
||||
TGoalVec solutions = getAllPossibleSubgoals();
|
||||
|
||||
if(solutions.empty())
|
||||
return sptr(Explore());
|
||||
|
||||
return fh->chooseSolution(solutions);
|
||||
}
|
||||
|
||||
|
||||
TGoalVec GatherTroops::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec solutions;
|
||||
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
int count = getCreaturesCount(t->getUpperArmy());
|
||||
|
||||
if(count >= this->value)
|
||||
{
|
||||
if(t->visitingHero)
|
||||
{
|
||||
solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get())));
|
||||
}
|
||||
else
|
||||
{
|
||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto creature = VLC->creh->creatures[objid];
|
||||
if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
|
||||
{
|
||||
auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
|
||||
if(!creatures)
|
||||
continue;
|
||||
|
||||
int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
|
||||
if(upgradeNumber < 0)
|
||||
continue;
|
||||
|
||||
BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
|
||||
if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->cost)) //this assumes only creatures with dwellings are assigned to faction
|
||||
{
|
||||
solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid)));
|
||||
}
|
||||
/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
|
||||
{
|
||||
return sptr(BuildThis(bid, t).setpriority(priority));
|
||||
}*/
|
||||
}
|
||||
}
|
||||
for(auto obj : ai->visitableObjs)
|
||||
{
|
||||
auto d = dynamic_cast<const CGDwelling *>(obj);
|
||||
|
||||
if(!d || obj->ID == Obj::TOWN)
|
||||
continue;
|
||||
|
||||
for(auto creature : d->creatures)
|
||||
{
|
||||
if(creature.first) //there are more than 0 creatures avaliabe
|
||||
{
|
||||
for(auto type : creature.second)
|
||||
{
|
||||
if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
|
||||
vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return solutions;
|
||||
//TODO: exchange troops between heroes
|
||||
}
|
||||
|
||||
bool GatherTroops::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if (!hero || hero == goal->hero) //we got army for desired hero or any hero
|
||||
if (goal->objid == objid) //same creature type //TODO: consider upgrades?
|
||||
if (goal->value >= value) //notify every time we get resources?
|
||||
return true;
|
||||
return false;
|
||||
}
|
43
AI/Nullkiller/Goals/GatherTroops.h
Normal file
43
AI/Nullkiller/Goals/GatherTroops.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* GatherTroops.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT GatherTroops : public CGoal<GatherTroops>
|
||||
{
|
||||
public:
|
||||
GatherTroops()
|
||||
: CGoal(Goals::GATHER_TROOPS)
|
||||
{
|
||||
priority = 2;
|
||||
}
|
||||
GatherTroops(int type, int val)
|
||||
: CGoal(Goals::GATHER_TROOPS)
|
||||
{
|
||||
objid = type;
|
||||
value = val;
|
||||
priority = 2;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const GatherTroops & other) const override;
|
||||
|
||||
private:
|
||||
int getCreaturesCount(const CArmedInstance * army);
|
||||
};
|
||||
}
|
31
AI/Nullkiller/Goals/GetArtOfType.cpp
Normal file
31
AI/Nullkiller/Goals/GetArtOfType.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* GetArtOfType.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 "GetArtOfType.h"
|
||||
#include "FindObj.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool GetArtOfType::operator==(const GetArtOfType & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.objid == objid;
|
||||
}
|
||||
|
||||
TSubgoal GetArtOfType::whatToDoToAchieve()
|
||||
{
|
||||
return sptr(FindObj(Obj::ARTIFACT, aid));
|
||||
}
|
40
AI/Nullkiller/Goals/GetArtOfType.h
Normal file
40
AI/Nullkiller/Goals/GetArtOfType.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* GetArtOfType.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT GetArtOfType : public CGoal<GetArtOfType>
|
||||
{
|
||||
public:
|
||||
GetArtOfType()
|
||||
: CGoal(Goals::GET_ART_TYPE)
|
||||
{
|
||||
}
|
||||
GetArtOfType(int type)
|
||||
: CGoal(Goals::GET_ART_TYPE)
|
||||
{
|
||||
aid = type;
|
||||
priority = 2;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const GetArtOfType & other) const override;
|
||||
};
|
||||
}
|
34
AI/Nullkiller/Goals/Goals.h
Normal file
34
AI/Nullkiller/Goals/Goals.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Goals.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
#include "Invalid.h"
|
||||
#include "BuildBoat.h"
|
||||
#include "Build.h"
|
||||
#include "BuildThis.h"
|
||||
#include "Conquer.h"
|
||||
#include "GatherArmy.h"
|
||||
#include "Win.h"
|
||||
#include "VisitObj.h"
|
||||
#include "VisitTile.h"
|
||||
#include "VisitHero.h"
|
||||
#include "Explore.h"
|
||||
#include "BuyArmy.h"
|
||||
#include "GatherTroops.h"
|
||||
#include "Trade.h"
|
||||
#include "CollectRes.h"
|
||||
#include "RecruitHero.h"
|
||||
#include "GetArtOfType.h"
|
||||
#include "ClearWayTo.h"
|
||||
#include "DigAtTile.h"
|
||||
#include "FindObj.h"
|
||||
#include "CompleteQuest.h"
|
||||
#include "AdventureSpellCast.h"
|
41
AI/Nullkiller/Goals/Invalid.h
Normal file
41
AI/Nullkiller/Goals/Invalid.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Invalid.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT Invalid : public CGoal<Invalid>
|
||||
{
|
||||
public:
|
||||
Invalid()
|
||||
: CGoal(Goals::INVALID)
|
||||
{
|
||||
priority = -1e10;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override
|
||||
{
|
||||
return iAmElementar();
|
||||
}
|
||||
|
||||
virtual bool operator==(const Invalid & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
38
AI/Nullkiller/Goals/RecruitHero.cpp
Normal file
38
AI/Nullkiller/Goals/RecruitHero.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* RecruitHero.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
TSubgoal RecruitHero::whatToDoToAchieve()
|
||||
{
|
||||
const CGTownInstance * t = ai->findTownWithTavern();
|
||||
if(!t)
|
||||
return sptr(BuildThis(BuildingID::TAVERN).setpriority(2));
|
||||
|
||||
TResources res;
|
||||
res[Res::GOLD] = GameConstants::HERO_GOLD_COST;
|
||||
return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
|
||||
}
|
41
AI/Nullkiller/Goals/RecruitHero.h
Normal file
41
AI/Nullkiller/Goals/RecruitHero.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* RecruitHero.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT RecruitHero : public CGoal<RecruitHero>
|
||||
{
|
||||
public:
|
||||
RecruitHero()
|
||||
: CGoal(Goals::RECRUIT_HERO)
|
||||
{
|
||||
priority = 1;
|
||||
}
|
||||
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
|
||||
virtual bool operator==(const RecruitHero & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
23
AI/Nullkiller/Goals/Trade.cpp
Normal file
23
AI/Nullkiller/Goals/Trade.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Trade.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 "Trade.h"
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool Trade::operator==(const Trade & other) const
|
||||
{
|
||||
return resID == other.resID;
|
||||
}
|
||||
|
||||
TSubgoal Trade::whatToDoToAchieve()
|
||||
{
|
||||
return iAmElementar();
|
||||
}
|
38
AI/Nullkiller/Goals/Trade.h
Normal file
38
AI/Nullkiller/Goals/Trade.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Trade.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT Trade : public CGoal<Trade>
|
||||
{
|
||||
public:
|
||||
Trade()
|
||||
: CGoal(Goals::TRADE)
|
||||
{
|
||||
}
|
||||
Trade(int rid, int val, int Objid)
|
||||
: CGoal(Goals::TRADE)
|
||||
{
|
||||
resID = rid;
|
||||
value = val;
|
||||
objid = Objid;
|
||||
priority = 3; //trading is instant, but picking resources is free
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const Trade & other) const override;
|
||||
};
|
||||
}
|
74
AI/Nullkiller/Goals/VisitHero.cpp
Normal file
74
AI/Nullkiller/Goals/VisitHero.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* VisitHero.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 "VisitHero.h"
|
||||
#include "Explore.h"
|
||||
#include "Invalid.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool VisitHero::operator==(const VisitHero & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.objid == objid;
|
||||
}
|
||||
|
||||
std::string VisitHero::completeMessage() const
|
||||
{
|
||||
return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
|
||||
}
|
||||
|
||||
TSubgoal VisitHero::whatToDoToAchieve()
|
||||
{
|
||||
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
|
||||
if(!obj)
|
||||
return sptr(Explore());
|
||||
int3 pos = obj->visitablePos();
|
||||
|
||||
if(hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
|
||||
{
|
||||
if(hero->visitablePos() == pos)
|
||||
logAi->error("Hero %s tries to visit himself.", hero.name);
|
||||
else
|
||||
{
|
||||
//can't use VISIT_TILE here as tile appears blocked by target hero
|
||||
//FIXME: elementar goal should not be abstract
|
||||
return sptr(VisitHero(objid).sethero(hero).settile(pos).setisElementar(true));
|
||||
}
|
||||
}
|
||||
return sptr(Invalid());
|
||||
}
|
||||
|
||||
bool VisitHero::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
//TODO: VisitObj shoudl not be used for heroes, but...
|
||||
if(goal->goalType == VISIT_TILE)
|
||||
{
|
||||
auto obj = cb->getObj(ObjectInstanceID(objid));
|
||||
if (!obj)
|
||||
{
|
||||
logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid);
|
||||
return false;
|
||||
}
|
||||
return obj->visitablePos() == goal->tile;
|
||||
}
|
||||
return false;
|
||||
}
|
42
AI/Nullkiller/Goals/VisitHero.h
Normal file
42
AI/Nullkiller/Goals/VisitHero.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* VisitHero.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT VisitHero : public CGoal<VisitHero>
|
||||
{
|
||||
public:
|
||||
VisitHero()
|
||||
: CGoal(Goals::VISIT_HERO)
|
||||
{
|
||||
}
|
||||
VisitHero(int hid)
|
||||
: CGoal(Goals::VISIT_HERO)
|
||||
{
|
||||
objid = hid;
|
||||
priority = 4;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitHero & other) const override;
|
||||
};
|
||||
}
|
118
AI/Nullkiller/Goals/VisitObj.cpp
Normal file
118
AI/Nullkiller/Goals/VisitObj.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* VisitObj.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool VisitObj::operator==(const VisitObj & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.objid == objid;
|
||||
}
|
||||
|
||||
std::string VisitObj::completeMessage() const
|
||||
{
|
||||
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
|
||||
}
|
||||
|
||||
TGoalVec VisitObj::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec goalList;
|
||||
const CGObjectInstance * obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if(!obj)
|
||||
{
|
||||
throw cannotFulfillGoalException("Object is missing - goal is invalid now!");
|
||||
}
|
||||
|
||||
int3 pos = obj->visitablePos();
|
||||
if(hero)
|
||||
{
|
||||
if(ai->isAccessibleForHero(pos, hero))
|
||||
{
|
||||
if(isSafeToVisit(hero, pos))
|
||||
goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(hero)));
|
||||
else
|
||||
goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
|
||||
|
||||
return goalList;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto potentialVisitor : cb->getHeroesInfo())
|
||||
{
|
||||
if(ai->isAccessibleForHero(pos, potentialVisitor))
|
||||
{
|
||||
if(isSafeToVisit(potentialVisitor, pos))
|
||||
goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(potentialVisitor)));
|
||||
else
|
||||
goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true)));
|
||||
}
|
||||
}
|
||||
if(!goalList.empty())
|
||||
{
|
||||
return goalList;
|
||||
}
|
||||
}
|
||||
|
||||
goalList.push_back(sptr(ClearWayTo(pos)));
|
||||
return goalList;
|
||||
}
|
||||
|
||||
TSubgoal VisitObj::whatToDoToAchieve()
|
||||
{
|
||||
auto bestGoal = fh->chooseSolution(getAllPossibleSubgoals());
|
||||
|
||||
if(bestGoal->goalType == VISIT_OBJ && bestGoal->hero)
|
||||
bestGoal->setisElementar(true);
|
||||
|
||||
return bestGoal;
|
||||
}
|
||||
|
||||
VisitObj::VisitObj(int Objid)
|
||||
: CGoal(VISIT_OBJ)
|
||||
{
|
||||
objid = Objid;
|
||||
auto obj = ai->myCb->getObjInstance(ObjectInstanceID(objid));
|
||||
if(obj)
|
||||
tile = obj->visitablePos();
|
||||
else
|
||||
logAi->error("VisitObj constructed with invalid object instance %d", Objid);
|
||||
|
||||
priority = 3;
|
||||
}
|
||||
|
||||
bool VisitObj::fulfillsMe(TSubgoal goal)
|
||||
{
|
||||
if(goal->goalType == VISIT_TILE)
|
||||
{
|
||||
if (!hero || hero == goal->hero)
|
||||
{
|
||||
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if (obj && obj->visitablePos() == goal->tile) //object could be removed
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
32
AI/Nullkiller/Goals/VisitObj.h
Normal file
32
AI/Nullkiller/Goals/VisitObj.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* VisitObj.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT VisitObj : public CGoal<VisitObj> //this goal was previously known as GetObj
|
||||
{
|
||||
public:
|
||||
VisitObj() = delete; // empty constructor not allowed
|
||||
VisitObj(int Objid);
|
||||
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitObj & other) const override;
|
||||
};
|
||||
}
|
98
AI/Nullkiller/Goals/VisitTile.cpp
Normal file
98
AI/Nullkiller/Goals/VisitTile.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* VisitTile.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool VisitTile::operator==(const VisitTile & other) const
|
||||
{
|
||||
return other.hero.h == hero.h && other.tile == tile;
|
||||
}
|
||||
|
||||
std::string VisitTile::completeMessage() const
|
||||
{
|
||||
return "Hero " + hero.get()->name + " visited tile " + tile.toString();
|
||||
}
|
||||
|
||||
TSubgoal VisitTile::whatToDoToAchieve()
|
||||
{
|
||||
auto ret = fh->chooseSolution(getAllPossibleSubgoals());
|
||||
|
||||
if(ret->hero)
|
||||
{
|
||||
if(isSafeToVisit(ret->hero, tile) && ai->isAccessibleForHero(tile, ret->hero))
|
||||
{
|
||||
ret->setisElementar(true);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sptr(GatherArmy(fh->evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT)
|
||||
.sethero(ret->hero).setisAbstract(true));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TGoalVec VisitTile::getAllPossibleSubgoals()
|
||||
{
|
||||
assert(cb->isInTheMap(tile));
|
||||
|
||||
TGoalVec ret;
|
||||
if(!cb->isVisible(tile))
|
||||
ret.push_back(sptr(Explore())); //what sense does it make?
|
||||
else
|
||||
{
|
||||
std::vector<const CGHeroInstance *> heroes;
|
||||
if(hero)
|
||||
heroes.push_back(hero.h); //use assigned hero if any
|
||||
else
|
||||
heroes = cb->getHeroesInfo(); //use most convenient hero
|
||||
|
||||
for(auto h : heroes)
|
||||
{
|
||||
if(ai->isAccessibleForHero(tile, h))
|
||||
ret.push_back(sptr(VisitTile(tile).sethero(h)));
|
||||
}
|
||||
if(ai->canRecruitAnyHero())
|
||||
ret.push_back(sptr(RecruitHero()));
|
||||
}
|
||||
if(ret.empty())
|
||||
{
|
||||
auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
|
||||
if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
|
||||
{
|
||||
if(hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
|
||||
ret.push_back(sptr(VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
|
||||
else
|
||||
throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier
|
||||
}
|
||||
else
|
||||
ret.push_back(sptr(ClearWayTo(tile)));
|
||||
}
|
||||
|
||||
//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
|
||||
return ret;
|
||||
}
|
37
AI/Nullkiller/Goals/VisitTile.h
Normal file
37
AI/Nullkiller/Goals/VisitTile.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* VisitTile.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT VisitTile : public CGoal<VisitTile>
|
||||
//tile, in conjunction with hero elementar; assumes tile is reachable
|
||||
{
|
||||
public:
|
||||
VisitTile() {} // empty constructor not allowed
|
||||
|
||||
VisitTile(int3 Tile)
|
||||
: CGoal(Goals::VISIT_TILE)
|
||||
{
|
||||
tile = Tile;
|
||||
priority = 5;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitTile & other) const override;
|
||||
};
|
||||
}
|
191
AI/Nullkiller/Goals/Win.cpp
Normal file
191
AI/Nullkiller/Goals/Win.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Win.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../ResourceManager.h"
|
||||
#include "../BuildingManager.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
TSubgoal Win::whatToDoToAchieve()
|
||||
{
|
||||
auto toBool = [=](const EventCondition &)
|
||||
{
|
||||
// TODO: proper implementation
|
||||
// Right now even already fulfilled goals will be included into generated list
|
||||
// Proper check should test if event condition is already fulfilled
|
||||
// Easiest way to do this is to call CGameState::checkForVictory but this function should not be
|
||||
// used on client side or in AI code
|
||||
return false;
|
||||
};
|
||||
|
||||
std::vector<EventCondition> goals;
|
||||
|
||||
for(const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
|
||||
{
|
||||
//TODO: try to eliminate human player(s) using loss conditions that have isHuman element
|
||||
|
||||
if(event.effect.type == EventEffect::VICTORY)
|
||||
{
|
||||
boost::range::copy(event.trigger.getFulfillmentCandidates(toBool), std::back_inserter(goals));
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: instead of returning first encountered goal AI should generate list of possible subgoals
|
||||
for(const EventCondition & goal : goals)
|
||||
{
|
||||
switch(goal.condition)
|
||||
{
|
||||
case EventCondition::HAVE_ARTIFACT:
|
||||
return sptr(GetArtOfType(goal.objectType));
|
||||
case EventCondition::DESTROY:
|
||||
{
|
||||
if(goal.object)
|
||||
{
|
||||
auto obj = cb->getObj(goal.object->id);
|
||||
if(obj)
|
||||
if(obj->getOwner() == ai->playerID) //we can't capture our own object
|
||||
return sptr(Conquer());
|
||||
|
||||
|
||||
return sptr(VisitObj(goal.object->id.getNum()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: destroy all objects of type goal.objectType
|
||||
// This situation represents "kill all creatures" condition from H3
|
||||
break;
|
||||
}
|
||||
}
|
||||
case EventCondition::HAVE_BUILDING:
|
||||
{
|
||||
// TODO build other buildings apart from Grail
|
||||
// goal.objectType = buidingID to build
|
||||
// goal.object = optional, town in which building should be built
|
||||
// Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
|
||||
|
||||
if(goal.objectType == BuildingID::GRAIL)
|
||||
{
|
||||
if(auto h = ai->getHeroWithGrail())
|
||||
{
|
||||
//hero is in a town that can host Grail
|
||||
if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
|
||||
{
|
||||
const CGTownInstance * t = h->visitedTown;
|
||||
return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto towns = cb->getTownsInfo();
|
||||
towns.erase(boost::remove_if(towns,
|
||||
[](const CGTownInstance * t) -> bool
|
||||
{
|
||||
return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
|
||||
}),
|
||||
towns.end());
|
||||
boost::sort(towns, CDistanceSorter(h.get()));
|
||||
if(towns.size())
|
||||
{
|
||||
return sptr(VisitTile(towns.front()->visitablePos()).sethero(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
double ratio = 0;
|
||||
// maybe make this check a bit more complex? For example:
|
||||
// 0.75 -> dig randomly within 3 tiles radius
|
||||
// 0.85 -> radius now 2 tiles
|
||||
// 0.95 -> 1 tile radius, position is fully known
|
||||
// AFAIK H3 AI does something like this
|
||||
int3 grailPos = cb->getGrailPos(&ratio);
|
||||
if(ratio > 0.99)
|
||||
{
|
||||
return sptr(DigAtTile(grailPos));
|
||||
} //TODO: use FIND_OBJ
|
||||
else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
|
||||
return sptr(VisitObj(obj->id.getNum()));
|
||||
else
|
||||
return sptr(Explore());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EventCondition::CONTROL:
|
||||
{
|
||||
if(goal.object)
|
||||
{
|
||||
auto objRelations = cb->getPlayerRelations(ai->playerID, goal.object->tempOwner);
|
||||
|
||||
if(objRelations == PlayerRelations::ENEMIES)
|
||||
{
|
||||
return sptr(VisitObj(goal.object->id.getNum()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Defance
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: control all objects of type "goal.objectType"
|
||||
// Represents H3 condition "Flag all mines"
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case EventCondition::HAVE_RESOURCES:
|
||||
//TODO mines? piles? marketplace?
|
||||
//save?
|
||||
return sptr(CollectRes(static_cast<Res::ERes>(goal.objectType), goal.value));
|
||||
case EventCondition::HAVE_CREATURES:
|
||||
return sptr(GatherTroops(goal.objectType, goal.value));
|
||||
case EventCondition::TRANSPORT:
|
||||
{
|
||||
//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
|
||||
// Represents "transport artifact" condition:
|
||||
// goal.objectType = type of artifact
|
||||
// goal.object = destination-town where artifact should be transported
|
||||
break;
|
||||
}
|
||||
case EventCondition::STANDARD_WIN:
|
||||
return sptr(Conquer());
|
||||
|
||||
// Conditions that likely don't need any implementation
|
||||
case EventCondition::DAYS_PASSED:
|
||||
break; // goal.value = number of days for condition to trigger
|
||||
case EventCondition::DAYS_WITHOUT_TOWN:
|
||||
break; // goal.value = number of days to trigger this
|
||||
case EventCondition::IS_HUMAN:
|
||||
break; // Should be only used in calculation of candidates (see toBool lambda)
|
||||
case EventCondition::CONST_VALUE:
|
||||
break;
|
||||
|
||||
case EventCondition::HAVE_0:
|
||||
case EventCondition::HAVE_BUILDING_0:
|
||||
case EventCondition::DESTROY_0:
|
||||
//TODO: support new condition format
|
||||
return sptr(Conquer());
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
return sptr(Invalid());
|
||||
}
|
39
AI/Nullkiller/Goals/Win.h
Normal file
39
AI/Nullkiller/Goals/Win.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Win.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT Win : public CGoal<Win>
|
||||
{
|
||||
public:
|
||||
Win()
|
||||
: CGoal(Goals::WIN)
|
||||
{
|
||||
priority = 100;
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
|
||||
virtual bool operator==(const Win & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
135
AI/Nullkiller/MapObjectsEvaluator.cpp
Normal file
135
AI/Nullkiller/MapObjectsEvaluator.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
#include "StdInc.h"
|
||||
#include "MapObjectsEvaluator.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapObjects/MiscObjects.h"
|
||||
#include "../../lib/CRandomGenerator.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
|
||||
MapObjectsEvaluator & MapObjectsEvaluator::getInstance()
|
||||
{
|
||||
static std::unique_ptr<MapObjectsEvaluator> singletonInstance;
|
||||
if(singletonInstance == nullptr)
|
||||
singletonInstance.reset(new MapObjectsEvaluator());
|
||||
|
||||
return *(singletonInstance.get());
|
||||
}
|
||||
|
||||
MapObjectsEvaluator::MapObjectsEvaluator()
|
||||
{
|
||||
for(auto primaryID : VLC->objtypeh->knownObjects())
|
||||
{
|
||||
for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
|
||||
{
|
||||
auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
|
||||
if(!handler->isStaticObject())
|
||||
{
|
||||
if(handler->getAiValue() != boost::none)
|
||||
{
|
||||
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = handler->getAiValue().get();
|
||||
}
|
||||
else if(VLC->objtypeh->getObjGroupAiValue(primaryID) != boost::none) //if value is not initialized - fallback to default value for this object family if it exists
|
||||
{
|
||||
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = VLC->objtypeh->getObjGroupAiValue(primaryID).get();
|
||||
}
|
||||
else //some default handling when aiValue not found, objects that require advanced properties (unavailable from handler) get their value calculated in getObjectValue
|
||||
{
|
||||
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<int> MapObjectsEvaluator::getObjectValue(int primaryID, int secondaryID) const
|
||||
{
|
||||
CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
|
||||
auto object = objectDatabase.find(internalIdentifier);
|
||||
if(object != objectDatabase.end())
|
||||
return object->second;
|
||||
|
||||
logGlobal->trace("Unknown object for AI, ID: " + std::to_string(primaryID) + ", SubID: " + std::to_string(secondaryID));
|
||||
return boost::optional<int>();
|
||||
}
|
||||
|
||||
boost::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance * obj) const
|
||||
{
|
||||
if(obj->ID == Obj::HERO)
|
||||
{
|
||||
//special case handling: in-game heroes have hero ID as object subID, but when reading configs available hero object subID's are hero classes
|
||||
auto hero = dynamic_cast<const CGHeroInstance*>(obj);
|
||||
return getObjectValue(obj->ID, hero->type->heroClass->id);
|
||||
}
|
||||
if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4)
|
||||
{
|
||||
auto dwelling = dynamic_cast<const CGDwelling *>(obj);
|
||||
int aiValue = 0;
|
||||
for(auto & creLevel : dwelling->creatures)
|
||||
{
|
||||
for(auto & creatureID : creLevel.second)
|
||||
{
|
||||
auto creature = VLC->creh->creatures[creatureID];
|
||||
aiValue += (creature->AIValue * creature->growth);
|
||||
}
|
||||
}
|
||||
return aiValue;
|
||||
}
|
||||
else if(obj->ID == Obj::ARTIFACT)
|
||||
{
|
||||
auto artifactObject = dynamic_cast<const CGArtifact *>(obj);
|
||||
switch(artifactObject->storedArtifact->artType->aClass)
|
||||
{
|
||||
case CArtifact::EartClass::ART_TREASURE:
|
||||
return 2000;
|
||||
case CArtifact::EartClass::ART_MINOR:
|
||||
return 5000;
|
||||
case CArtifact::EartClass::ART_MAJOR:
|
||||
return 10000;
|
||||
case CArtifact::EartClass::ART_RELIC:
|
||||
return 20000;
|
||||
case CArtifact::EartClass::ART_SPECIAL:
|
||||
return 20000;
|
||||
default:
|
||||
return 0; //invalid artifact class
|
||||
}
|
||||
}
|
||||
else if(obj->ID == Obj::SPELL_SCROLL)
|
||||
{
|
||||
auto scrollObject = dynamic_cast<const CGArtifact *>(obj);
|
||||
auto spell = scrollObject->storedArtifact->getGivenSpellID().toSpell();
|
||||
if(spell)
|
||||
{
|
||||
switch(spell->getLevel())
|
||||
{
|
||||
case 0: return 0; //scroll with creature ability? Let's assume it is useless
|
||||
case 1: return 1000;
|
||||
case 2: return 2000;
|
||||
case 3: return 5000;
|
||||
case 4: return 10000;
|
||||
case 5: return 20000;
|
||||
default: logAi->warn("AI detected spell scroll with spell level %s", spell->getLevel());
|
||||
}
|
||||
}
|
||||
else
|
||||
logAi->warn("AI found spell scroll with invalid spell ID: %s", scrollObject->storedArtifact->getGivenSpellID());
|
||||
}
|
||||
|
||||
return getObjectValue(obj->ID, obj->subID);
|
||||
}
|
||||
|
||||
void MapObjectsEvaluator::addObjectData(int primaryID, int secondaryID, int value) //by current design it updates value if already in AI database
|
||||
{
|
||||
CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
|
||||
objectDatabase[internalIdentifier] = value;
|
||||
}
|
||||
|
||||
void MapObjectsEvaluator::removeObjectData(int primaryID, int secondaryID)
|
||||
{
|
||||
CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
|
||||
vstd::erase_if_present(objectDatabase, internalIdentifier);
|
||||
}
|
||||
|
26
AI/Nullkiller/MapObjectsEvaluator.h
Normal file
26
AI/Nullkiller/MapObjectsEvaluator.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* MapObjectsEvaluator.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 "../../lib/mapObjects/CObjectClassesHandler.h"
|
||||
|
||||
class MapObjectsEvaluator
|
||||
{
|
||||
private:
|
||||
std::map<CompoundMapObjectID, int> objectDatabase; //value for each object type
|
||||
|
||||
public:
|
||||
MapObjectsEvaluator();
|
||||
static MapObjectsEvaluator & getInstance();
|
||||
boost::optional<int> getObjectValue(int primaryID, int secondaryID) const;
|
||||
boost::optional<int> getObjectValue(const CGObjectInstance * obj) const;
|
||||
void addObjectData(int primaryID, int secondaryID, int value);
|
||||
void removeObjectData(int primaryID, int secondaryID);
|
||||
};
|
||||
|
413
AI/Nullkiller/Pathfinding/AINodeStorage.cpp
Normal file
413
AI/Nullkiller/Pathfinding/AINodeStorage.cpp
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
* AINodeStorage.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 "AINodeStorage.h"
|
||||
#include "Actions/TownPortalAction.h"
|
||||
#include "../Goals/Goals.h"
|
||||
#include "../../../CCallback.h"
|
||||
#include "../../../lib/mapping/CMap.h"
|
||||
#include "../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../../lib/PathfinderUtil.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
|
||||
AINodeStorage::AINodeStorage(const int3 & Sizes)
|
||||
: sizes(Sizes)
|
||||
{
|
||||
nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]);
|
||||
dangerEvaluator.reset(new FuzzyHelper());
|
||||
}
|
||||
|
||||
AINodeStorage::~AINodeStorage() = default;
|
||||
|
||||
void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero)
|
||||
{
|
||||
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
|
||||
|
||||
int3 pos;
|
||||
const int3 sizes = gs->getMapSize();
|
||||
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap;
|
||||
const PlayerColor player = hero->tempOwner;
|
||||
|
||||
//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
|
||||
const bool useFlying = options.useFlying;
|
||||
const bool useWaterWalking = options.useWaterWalking;
|
||||
|
||||
for(pos.x=0; pos.x < sizes.x; ++pos.x)
|
||||
{
|
||||
for(pos.y=0; pos.y < sizes.y; ++pos.y)
|
||||
{
|
||||
for(pos.z=0; pos.z < sizes.z; ++pos.z)
|
||||
{
|
||||
const TerrainTile * tile = &gs->map->getTile(pos);
|
||||
switch(tile->terType)
|
||||
{
|
||||
case ETerrainType::ROCK:
|
||||
break;
|
||||
|
||||
case ETerrainType::WATER:
|
||||
resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
|
||||
if(useFlying)
|
||||
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
|
||||
if(useWaterWalking)
|
||||
resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
|
||||
break;
|
||||
|
||||
default:
|
||||
resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
|
||||
if(useFlying)
|
||||
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
|
||||
{
|
||||
return static_cast<const AIPathNode *>(node);
|
||||
}
|
||||
|
||||
void AINodeStorage::updateAINode(CGPathNode * node, std::function<void(AIPathNode *)> updater)
|
||||
{
|
||||
auto aiNode = static_cast<AIPathNode *>(node);
|
||||
|
||||
updater(aiNode);
|
||||
}
|
||||
|
||||
bool AINodeStorage::isBattleNode(const CGPathNode * node) const
|
||||
{
|
||||
return (getAINode(node)->chainMask & BATTLE_CHAIN) > 0;
|
||||
}
|
||||
|
||||
boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber)
|
||||
{
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
||||
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
if(node.chainMask == chainNumber)
|
||||
{
|
||||
return &node;
|
||||
}
|
||||
|
||||
if(node.chainMask == 0)
|
||||
{
|
||||
node.chainMask = chainNumber;
|
||||
|
||||
return &node;
|
||||
}
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
CGPathNode * AINodeStorage::getInitialNode()
|
||||
{
|
||||
auto hpos = hero->getPosition(false);
|
||||
auto initialNode =
|
||||
getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN)
|
||||
.get();
|
||||
|
||||
initialNode->turns = 0;
|
||||
initialNode->moveRemains = hero->movement;
|
||||
initialNode->danger = 0;
|
||||
initialNode->cost = 0.0;
|
||||
|
||||
return initialNode;
|
||||
}
|
||||
|
||||
void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility)
|
||||
{
|
||||
for(int i = 0; i < NUM_CHAINS; i++)
|
||||
{
|
||||
AIPathNode & heroNode = nodes[coord.x][coord.y][coord.z][layer][i];
|
||||
|
||||
heroNode.chainMask = 0;
|
||||
heroNode.danger = 0;
|
||||
heroNode.manaCost = 0;
|
||||
heroNode.specialAction.reset();
|
||||
heroNode.update(coord, layer, accessibility);
|
||||
}
|
||||
}
|
||||
|
||||
void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
|
||||
{
|
||||
const AIPathNode * srcNode = getAINode(source.node);
|
||||
|
||||
updateAINode(destination.node, [&](AIPathNode * dstNode)
|
||||
{
|
||||
dstNode->moveRemains = destination.movementLeft;
|
||||
dstNode->turns = destination.turn;
|
||||
dstNode->cost = destination.cost;
|
||||
dstNode->danger = srcNode->danger;
|
||||
dstNode->action = destination.action;
|
||||
dstNode->theNodeBefore = srcNode->theNodeBefore;
|
||||
dstNode->manaCost = srcNode->manaCost;
|
||||
|
||||
if(dstNode->specialAction)
|
||||
{
|
||||
dstNode->specialAction->applyOnDestination(getHero(), destination, source, dstNode, srcNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
||||
const PathNodeInfo & source,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<CGPathNode *> neighbours;
|
||||
neighbours.reserve(16);
|
||||
const AIPathNode * srcNode = getAINode(source.node);
|
||||
auto accessibleNeighbourTiles = pathfinderHelper->getNeighbourTiles(source);
|
||||
|
||||
for(auto & neighbour : accessibleNeighbourTiles)
|
||||
{
|
||||
for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1))
|
||||
{
|
||||
auto nextNode = getOrCreateNode(neighbour, i, srcNode->chainMask);
|
||||
|
||||
if(!nextNode || nextNode.get()->accessible == CGPathNode::NOT_SET)
|
||||
continue;
|
||||
|
||||
neighbours.push_back(nextNode.get());
|
||||
}
|
||||
}
|
||||
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
void AINodeStorage::setHero(HeroPtr heroPtr, const VCAI * _ai)
|
||||
{
|
||||
hero = heroPtr.get();
|
||||
cb = _ai->myCb.get();
|
||||
ai = _ai;
|
||||
}
|
||||
|
||||
std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
|
||||
const PathNodeInfo & source,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<CGPathNode *> neighbours;
|
||||
|
||||
if(source.isNodeObjectVisitable())
|
||||
{
|
||||
auto accessibleExits = pathfinderHelper->getTeleportExits(source);
|
||||
auto srcNode = getAINode(source.node);
|
||||
|
||||
for(auto & neighbour : accessibleExits)
|
||||
{
|
||||
auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask);
|
||||
|
||||
if(!node)
|
||||
continue;
|
||||
|
||||
neighbours.push_back(node.get());
|
||||
}
|
||||
}
|
||||
|
||||
if(hero->getPosition(false) == source.coord)
|
||||
{
|
||||
calculateTownPortalTeleportations(source, neighbours);
|
||||
}
|
||||
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
void AINodeStorage::calculateTownPortalTeleportations(
|
||||
const PathNodeInfo & source,
|
||||
std::vector<CGPathNode *> & neighbours)
|
||||
{
|
||||
SpellID spellID = SpellID::TOWN_PORTAL;
|
||||
const CSpell * townPortal = spellID.toSpell();
|
||||
auto srcNode = getAINode(source.node);
|
||||
|
||||
if(hero->canCastThisSpell(townPortal) && hero->mana >= hero->getSpellCost(townPortal))
|
||||
{
|
||||
auto towns = cb->getTownsInfo(false);
|
||||
|
||||
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
||||
{
|
||||
return cb->getPlayerRelations(hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
||||
});
|
||||
|
||||
if(!towns.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Copy/Paste from TownPortalMechanics
|
||||
auto skillLevel = hero->getSpellSchoolLevel(townPortal);
|
||||
auto movementCost = GameConstants::BASE_MOVEMENT_COST * (skillLevel >= 3 ? 2 : 3);
|
||||
|
||||
if(hero->movement < movementCost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(skillLevel < SecSkillLevel::ADVANCED)
|
||||
{
|
||||
const CGTownInstance * nearestTown = *vstd::minElementByFun(towns, [&](const CGTownInstance * t) -> int
|
||||
{
|
||||
return hero->visitablePos().dist2dSQ(t->visitablePos());
|
||||
});
|
||||
|
||||
towns = std::vector<const CGTownInstance *>{ nearestTown };
|
||||
}
|
||||
|
||||
for(const CGTownInstance * targetTown : towns)
|
||||
{
|
||||
if(targetTown->visitingHero)
|
||||
continue;
|
||||
|
||||
auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN);
|
||||
|
||||
if(nodeOptional)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Adding town portal node at %s", targetTown->name);
|
||||
#endif
|
||||
|
||||
AIPathNode * node = nodeOptional.get();
|
||||
|
||||
node->theNodeBefore = source.node;
|
||||
node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown));
|
||||
node->moveRemains = source.node->moveRemains;
|
||||
|
||||
neighbours.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||
{
|
||||
auto pos = destination.coord;
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][EPathfindingLayer::LAND];
|
||||
auto destinationNode = getAINode(destination.node);
|
||||
|
||||
for(const AIPathNode & node : chains)
|
||||
{
|
||||
auto sameNode = node.chainMask == destinationNode->chainMask;
|
||||
if(sameNode || node.action == CGPathNode::ENodeAction::UNKNOWN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0)
|
||||
{
|
||||
if(node.cost < destinationNode->cost)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Block ineficient move %s:->%s, mask=%i, mp diff: %i",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString(),
|
||||
destinationNode->chainMask,
|
||||
node.moveRemains - destinationNode->moveRemains);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const
|
||||
{
|
||||
const AIPathNode & node = nodes[pos.x][pos.y][pos.z][layer][0];
|
||||
|
||||
return node.action != CGPathNode::ENodeAction::UNKNOWN;
|
||||
}
|
||||
|
||||
std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
|
||||
{
|
||||
std::vector<AIPath> paths;
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL];
|
||||
auto initialPos = hero->visitablePos();
|
||||
|
||||
for(const AIPathNode & node : chains)
|
||||
{
|
||||
if(node.action == CGPathNode::ENodeAction::UNKNOWN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AIPath path;
|
||||
const AIPathNode * current = &node;
|
||||
|
||||
while(current != nullptr && current->coord != initialPos)
|
||||
{
|
||||
AIPathNodeInfo pathNode;
|
||||
pathNode.cost = current->cost;
|
||||
pathNode.turns = current->turns;
|
||||
pathNode.danger = current->danger;
|
||||
pathNode.coord = current->coord;
|
||||
|
||||
path.nodes.push_back(pathNode);
|
||||
path.specialAction = current->specialAction;
|
||||
|
||||
current = getAINode(current->theNodeBefore);
|
||||
}
|
||||
|
||||
path.targetObjectDanger = evaluateDanger(pos);
|
||||
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
AIPath::AIPath()
|
||||
: nodes({})
|
||||
{
|
||||
}
|
||||
|
||||
int3 AIPath::firstTileToGet() const
|
||||
{
|
||||
if(nodes.size())
|
||||
{
|
||||
return nodes.back().coord;
|
||||
}
|
||||
|
||||
return int3(-1, -1, -1);
|
||||
}
|
||||
|
||||
uint64_t AIPath::getPathDanger() const
|
||||
{
|
||||
if(nodes.size())
|
||||
{
|
||||
return nodes.front().danger;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AIPath::movementCost() const
|
||||
{
|
||||
if(nodes.size())
|
||||
{
|
||||
return nodes.front().cost;
|
||||
}
|
||||
|
||||
// TODO: boost:optional?
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
uint64_t AIPath::getTotalDanger(HeroPtr hero) const
|
||||
{
|
||||
uint64_t pathDanger = getPathDanger();
|
||||
uint64_t danger = pathDanger > targetObjectDanger ? pathDanger : targetObjectDanger;
|
||||
|
||||
return danger;
|
||||
}
|
124
AI/Nullkiller/Pathfinding/AINodeStorage.h
Normal file
124
AI/Nullkiller/Pathfinding/AINodeStorage.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* AINodeStorage.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 "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
#include "Actions/ISpecialAction.h"
|
||||
|
||||
struct AIPathNode;
|
||||
|
||||
struct AIPathNode : public CGPathNode
|
||||
{
|
||||
uint32_t chainMask;
|
||||
uint64_t danger;
|
||||
uint32_t manaCost;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
};
|
||||
|
||||
struct AIPathNodeInfo
|
||||
{
|
||||
float cost;
|
||||
int turns;
|
||||
int3 coord;
|
||||
uint64_t danger;
|
||||
};
|
||||
|
||||
struct AIPath
|
||||
{
|
||||
std::vector<AIPathNodeInfo> nodes;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
uint64_t targetObjectDanger;
|
||||
|
||||
AIPath();
|
||||
|
||||
/// Gets danger of path excluding danger of visiting the target object like creature bank
|
||||
uint64_t getPathDanger() const;
|
||||
|
||||
/// Gets danger of path including danger of visiting the target object like creature bank
|
||||
uint64_t getTotalDanger(HeroPtr hero) const;
|
||||
|
||||
int3 firstTileToGet() const;
|
||||
|
||||
float movementCost() const;
|
||||
};
|
||||
|
||||
class AINodeStorage : public INodeStorage
|
||||
{
|
||||
private:
|
||||
int3 sizes;
|
||||
|
||||
/// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations)
|
||||
boost::multi_array<AIPathNode, 5> nodes;
|
||||
const CPlayerSpecificInfoCallback * cb;
|
||||
const VCAI * ai;
|
||||
const CGHeroInstance * hero;
|
||||
std::unique_ptr<FuzzyHelper> dangerEvaluator;
|
||||
|
||||
STRONG_INLINE
|
||||
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
|
||||
|
||||
public:
|
||||
/// more than 1 chain layer allows us to have more than 1 path to each tile so we can chose more optimal one.
|
||||
static const int NUM_CHAINS = 3;
|
||||
|
||||
// chain flags, can be combined
|
||||
static const int NORMAL_CHAIN = 1;
|
||||
static const int BATTLE_CHAIN = 2;
|
||||
static const int CAST_CHAIN = 4;
|
||||
static const int RESOURCE_CHAIN = 8;
|
||||
|
||||
AINodeStorage(const int3 & sizes);
|
||||
~AINodeStorage();
|
||||
|
||||
void initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero) override;
|
||||
|
||||
virtual CGPathNode * getInitialNode() override;
|
||||
|
||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
||||
const PathNodeInfo & source,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper) override;
|
||||
|
||||
virtual std::vector<CGPathNode *> calculateTeleportations(
|
||||
const PathNodeInfo & source,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper) override;
|
||||
|
||||
virtual void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) override;
|
||||
|
||||
const AIPathNode * getAINode(const CGPathNode * node) const;
|
||||
void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater);
|
||||
|
||||
bool isBattleNode(const CGPathNode * node) const;
|
||||
bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
||||
boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber);
|
||||
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
|
||||
bool isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const;
|
||||
|
||||
void setHero(HeroPtr heroPtr, const VCAI * ai);
|
||||
|
||||
const CGHeroInstance * getHero() const
|
||||
{
|
||||
return hero;
|
||||
}
|
||||
|
||||
uint64_t evaluateDanger(const int3 & tile) const
|
||||
{
|
||||
return dangerEvaluator->evaluateDanger(tile, hero, ai);
|
||||
}
|
||||
|
||||
private:
|
||||
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
|
||||
};
|
140
AI/Nullkiller/Pathfinding/AIPathfinder.cpp
Normal file
140
AI/Nullkiller/Pathfinding/AIPathfinder.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* AIPathfinder.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 "AIPathfinder.h"
|
||||
#include "AIPathfinderConfig.h"
|
||||
#include "../../../CCallback.h"
|
||||
#include "../../../lib/mapping/CMap.h"
|
||||
|
||||
std::vector<std::shared_ptr<AINodeStorage>> AIPathfinder::storagePool;
|
||||
std::map<HeroPtr, std::shared_ptr<AINodeStorage>> AIPathfinder::storageMap;
|
||||
|
||||
AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai)
|
||||
:cb(cb), ai(ai)
|
||||
{
|
||||
}
|
||||
|
||||
void AIPathfinder::init()
|
||||
{
|
||||
storagePool.clear();
|
||||
storageMap.clear();
|
||||
}
|
||||
|
||||
bool AIPathfinder::isTileAccessible(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
std::shared_ptr<const AINodeStorage> nodeStorage = getStorage(hero);
|
||||
|
||||
return nodeStorage->isTileAccessible(tile, EPathfindingLayer::LAND)
|
||||
|| nodeStorage->isTileAccessible(tile, EPathfindingLayer::SAIL);
|
||||
}
|
||||
|
||||
std::vector<AIPath> AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
std::shared_ptr<const AINodeStorage> nodeStorage = getStorage(hero);
|
||||
|
||||
const TerrainTile * tileInfo = cb->getTile(tile, false);
|
||||
|
||||
if(!tileInfo)
|
||||
{
|
||||
return std::vector<AIPath>();
|
||||
}
|
||||
|
||||
return nodeStorage->getChainInfo(tile, !tileInfo->isWater());
|
||||
}
|
||||
|
||||
void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
|
||||
{
|
||||
storageMap.clear();
|
||||
|
||||
auto calculatePaths = [&](const CGHeroInstance * hero, std::shared_ptr<AIPathfinding::AIPathfinderConfig> config)
|
||||
{
|
||||
logAi->debug("Recalculate paths for %s", hero->name);
|
||||
|
||||
cb->calculatePaths(config, hero);
|
||||
};
|
||||
|
||||
std::vector<Task> calculationTasks;
|
||||
|
||||
for(HeroPtr hero : heroes)
|
||||
{
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
|
||||
if(storageMap.size() < storagePool.size())
|
||||
{
|
||||
nodeStorage = storagePool.at(storageMap.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeStorage = std::make_shared<AINodeStorage>(cb->getMapSize());
|
||||
storagePool.push_back(nodeStorage);
|
||||
}
|
||||
|
||||
storageMap[hero] = nodeStorage;
|
||||
nodeStorage->setHero(hero, ai);
|
||||
|
||||
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
|
||||
|
||||
calculationTasks.push_back(std::bind(calculatePaths, hero.get(), config));
|
||||
}
|
||||
|
||||
int threadsCount = std::min(
|
||||
boost::thread::hardware_concurrency(),
|
||||
(uint32_t)calculationTasks.size());
|
||||
|
||||
if(threadsCount <= 1)
|
||||
{
|
||||
for(auto task : calculationTasks)
|
||||
{
|
||||
task();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CThreadHelper helper(&calculationTasks, threadsCount);
|
||||
|
||||
helper.run();
|
||||
}
|
||||
}
|
||||
|
||||
void AIPathfinder::updatePaths(const HeroPtr & hero)
|
||||
{
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
|
||||
if(!vstd::contains(storageMap, hero))
|
||||
{
|
||||
if(storageMap.size() < storagePool.size())
|
||||
{
|
||||
nodeStorage = storagePool.at(storageMap.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeStorage = std::make_shared<AINodeStorage>(cb->getMapSize());
|
||||
storagePool.push_back(nodeStorage);
|
||||
}
|
||||
|
||||
storageMap[hero] = nodeStorage;
|
||||
nodeStorage->setHero(hero, ai);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeStorage = storageMap.at(hero);
|
||||
}
|
||||
|
||||
logAi->debug("Recalculate paths for %s", hero->name);
|
||||
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
|
||||
|
||||
cb->calculatePaths(config, hero.get());
|
||||
}
|
||||
|
||||
std::shared_ptr<const AINodeStorage> AIPathfinder::getStorage(const HeroPtr & hero) const
|
||||
{
|
||||
return storageMap.at(hero);
|
||||
}
|
||||
|
33
AI/Nullkiller/Pathfinding/AIPathfinder.h
Normal file
33
AI/Nullkiller/Pathfinding/AIPathfinder.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* AIPathfinder.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 "AINodeStorage.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../VCAI.h"
|
||||
|
||||
class AIPathfinder
|
||||
{
|
||||
private:
|
||||
static std::vector<std::shared_ptr<AINodeStorage>> storagePool;
|
||||
static std::map<HeroPtr, std::shared_ptr<AINodeStorage>> storageMap;
|
||||
CPlayerSpecificInfoCallback * cb;
|
||||
VCAI * ai;
|
||||
|
||||
std::shared_ptr<const AINodeStorage> getStorage(const HeroPtr & hero) const;
|
||||
public:
|
||||
AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
|
||||
std::vector<AIPath> getPathInfo(const HeroPtr & hero, const int3 & tile) const;
|
||||
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
|
||||
void updatePaths(std::vector<HeroPtr> heroes);
|
||||
void updatePaths(const HeroPtr & heroes);
|
||||
void init();
|
||||
};
|
43
AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp
Normal file
43
AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* AIPathfinderConfig.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 "AIPathfinderConfig.h"
|
||||
#include "Rules/AILayerTransitionRule.h"
|
||||
#include "Rules/AIMovementAfterDestinationRule.h"
|
||||
#include "Rules/AIMovementToDestinationRule.h"
|
||||
#include "Rules/AIPreviousNodeRule.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
std::vector<std::shared_ptr<IPathfindingRule>> makeRuleset(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
VCAI * ai,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
{
|
||||
std::vector<std::shared_ptr<IPathfindingRule>> rules = {
|
||||
std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage),
|
||||
std::make_shared<DestinationActionRule>(),
|
||||
std::make_shared<AIMovementToDestinationRule>(nodeStorage),
|
||||
std::make_shared<MovementCostRule>(),
|
||||
std::make_shared<AIPreviousNodeRule>(nodeStorage),
|
||||
std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage)
|
||||
};
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
AIPathfinderConfig::AIPathfinderConfig(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
VCAI * ai,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage))
|
||||
{
|
||||
}
|
||||
}
|
26
AI/Nullkiller/Pathfinding/AIPathfinderConfig.h
Normal file
26
AI/Nullkiller/Pathfinding/AIPathfinderConfig.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* AIPathfinderConfig.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 "AINodeStorage.h"
|
||||
#include "../VCAI.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class AIPathfinderConfig : public PathfinderConfig
|
||||
{
|
||||
public:
|
||||
AIPathfinderConfig(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
VCAI * ai,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
};
|
||||
}
|
21
AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp
Normal file
21
AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* BattleAction.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "../../Goals/VisitTile.h"
|
||||
#include "BattleAction.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
Goals::TSubgoal BattleAction::whatToDo(const HeroPtr & hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
|
||||
}
|
||||
}
|
30
AI/Nullkiller/Pathfinding/Actions/BattleAction.h
Normal file
30
AI/Nullkiller/Pathfinding/Actions/BattleAction.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* BattleAction.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 "ISpecialAction.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class BattleAction : public ISpecialAction
|
||||
{
|
||||
private:
|
||||
const int3 targetTile;
|
||||
|
||||
public:
|
||||
BattleAction(const int3 targetTile)
|
||||
:targetTile(targetTile)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
};
|
||||
}
|
61
AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp
Normal file
61
AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* BoatActions.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "../../Goals/AdventureSpellCast.h"
|
||||
#include "../../Goals/BuildBoat.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
#include "BoatActions.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::BuildBoat(shipyard));
|
||||
}
|
||||
|
||||
Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
|
||||
}
|
||||
|
||||
void SummonBoatAction::applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
|
||||
dstMode->theNodeBefore = source.node;
|
||||
}
|
||||
|
||||
bool SummonBoatAction::isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Hero %s has %d mana and needed %d and already spent %d",
|
||||
hero->name,
|
||||
hero->mana,
|
||||
getManaCost(hero),
|
||||
source->manaCost);
|
||||
#endif
|
||||
|
||||
return hero->mana >= source->manaCost + getManaCost(hero);
|
||||
}
|
||||
|
||||
uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
|
||||
{
|
||||
SpellID summonBoat = SpellID::SUMMON_BOAT;
|
||||
|
||||
return hero->getSpellCost(summonBoat.toSpell());
|
||||
}
|
||||
}
|
72
AI/Nullkiller/Pathfinding/Actions/BoatActions.h
Normal file
72
AI/Nullkiller/Pathfinding/Actions/BoatActions.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* BoatActions.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 "ISpecialAction.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class VirtualBoatAction : public ISpecialAction
|
||||
{
|
||||
private:
|
||||
uint64_t specialChain;
|
||||
|
||||
public:
|
||||
VirtualBoatAction(uint64_t specialChain)
|
||||
:specialChain(specialChain)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t getSpecialChain() const
|
||||
{
|
||||
return specialChain;
|
||||
}
|
||||
};
|
||||
|
||||
class SummonBoatAction : public VirtualBoatAction
|
||||
{
|
||||
public:
|
||||
SummonBoatAction()
|
||||
:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const override;
|
||||
|
||||
bool isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const;
|
||||
|
||||
private:
|
||||
uint32_t getManaCost(const CGHeroInstance * hero) const;
|
||||
};
|
||||
|
||||
class BuildBoatAction : public VirtualBoatAction
|
||||
{
|
||||
private:
|
||||
const IShipyard * shipyard;
|
||||
|
||||
public:
|
||||
BuildBoatAction(const IShipyard * shipyard)
|
||||
:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
};
|
||||
}
|
31
AI/Nullkiller/Pathfinding/Actions/ISpecialAction.h
Normal file
31
AI/Nullkiller/Pathfinding/Actions/ISpecialAction.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ISpecialAction.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/AbstractGoal.h"
|
||||
|
||||
class AIPathNode;
|
||||
|
||||
class ISpecialAction
|
||||
{
|
||||
public:
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
}
|
||||
};
|
24
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp
Normal file
24
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* TownPortalAction.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "../../Goals/AdventureSpellCast.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
#include "TownPortalAction.h"
|
||||
|
||||
using namespace AIPathfinding;
|
||||
|
||||
Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const
|
||||
{
|
||||
const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
|
||||
|
||||
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL).settown(targetTown).settile(targetTown->visitablePos()));
|
||||
}
|
33
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.h
Normal file
33
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* TownPortalAction.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 "ISpecialAction.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../Goals/AdventureSpellCast.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class TownPortalAction : public ISpecialAction
|
||||
{
|
||||
private:
|
||||
const CGTownInstance * target;
|
||||
|
||||
public:
|
||||
TownPortalAction(const CGTownInstance * target)
|
||||
:target(target)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
|
||||
};
|
||||
}
|
247
AI/Nullkiller/Pathfinding/PathfindingManager.cpp
Normal file
247
AI/Nullkiller/Pathfinding/PathfindingManager.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* PathfindingManager.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 "PathfindingManager.h"
|
||||
#include "AIPathfinder.h"
|
||||
#include "AIPathfinderConfig.h"
|
||||
#include "../Goals/Goals.h"
|
||||
#include "../../../lib/CGameInfoCallback.h"
|
||||
#include "../../../lib/mapping/CMap.h"
|
||||
|
||||
PathfindingManager::PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI)
|
||||
: ai(AI), cb(CB)
|
||||
{
|
||||
}
|
||||
|
||||
void PathfindingManager::init(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
cb = CB;
|
||||
pathfinder.reset(new AIPathfinder(cb, ai));
|
||||
pathfinder->init();
|
||||
}
|
||||
|
||||
void PathfindingManager::setAI(VCAI * AI)
|
||||
{
|
||||
ai = AI;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitTile(const int3 & tile) const
|
||||
{
|
||||
Goals::TGoalVec result;
|
||||
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
result.reserve(heroes.size());
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
vstd::concatenate(result, howToVisitTile(hero, tile));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitObj(ObjectIdRef obj) const
|
||||
{
|
||||
Goals::TGoalVec result;
|
||||
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
result.reserve(heroes.size());
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
vstd::concatenate(result, howToVisitObj(hero, obj));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
|
||||
{
|
||||
auto result = findPath(hero, tile, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal
|
||||
{
|
||||
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
|
||||
});
|
||||
|
||||
for(Goals::TSubgoal solution : result)
|
||||
{
|
||||
solution->setparent(sptr(Goals::VisitTile(tile).sethero(hero).setevaluationContext(solution->evaluationContext)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
|
||||
{
|
||||
if(!obj)
|
||||
{
|
||||
return Goals::TGoalVec();
|
||||
}
|
||||
|
||||
int3 dest = obj->visitablePos();
|
||||
|
||||
auto result = findPath(hero, dest, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal
|
||||
{
|
||||
if(obj->ID.num == Obj::HERO && obj->getOwner() == hero->getOwner())
|
||||
return sptr(Goals::VisitHero(obj->id.getNum()).sethero(hero).setisAbstract(true));
|
||||
else
|
||||
return sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setisAbstract(true));
|
||||
});
|
||||
|
||||
for(Goals::TSubgoal solution : result)
|
||||
{
|
||||
solution->setparent(sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setevaluationContext(solution->evaluationContext)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
return pathfinder->getPathInfo(hero, tile);
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::findPath(
|
||||
HeroPtr hero,
|
||||
crint3 dest,
|
||||
bool allowGatherArmy,
|
||||
const std::function<Goals::TSubgoal(int3)> doVisitTile) const
|
||||
{
|
||||
Goals::TGoalVec result;
|
||||
boost::optional<uint64_t> armyValueRequired;
|
||||
uint64_t danger;
|
||||
|
||||
std::vector<AIPath> chainInfo = pathfinder->getPathInfo(hero, dest);
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Trying to find a way for %s to visit tile %s", hero->name, dest.toString());
|
||||
#endif
|
||||
|
||||
for(auto path : chainInfo)
|
||||
{
|
||||
int3 firstTileToGet = path.firstTileToGet();
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Path found size=%i, first tile=%s", path.nodes.size(), firstTileToGet.toString());
|
||||
#endif
|
||||
if(firstTileToGet.valid() && ai->isTileNotReserved(hero.get(), firstTileToGet))
|
||||
{
|
||||
danger = path.getTotalDanger(hero);
|
||||
|
||||
if(isSafeToVisit(hero, danger))
|
||||
{
|
||||
Goals::TSubgoal solution;
|
||||
|
||||
if(path.specialAction)
|
||||
{
|
||||
solution = path.specialAction->whatToDo(hero);
|
||||
}
|
||||
else
|
||||
{
|
||||
solution = dest == firstTileToGet
|
||||
? doVisitTile(firstTileToGet)
|
||||
: clearWayTo(hero, firstTileToGet);
|
||||
}
|
||||
|
||||
if(solution->invalid())
|
||||
continue;
|
||||
|
||||
if(solution->evaluationContext.danger < danger)
|
||||
solution->evaluationContext.danger = danger;
|
||||
|
||||
solution->evaluationContext.movementCost += path.movementCost();
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("It's safe for %s to visit tile %s with danger %s, goal %s", hero->name, dest.toString(), std::to_string(danger), solution->name());
|
||||
#endif
|
||||
result.push_back(solution);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!armyValueRequired || armyValueRequired > danger)
|
||||
{
|
||||
armyValueRequired = boost::make_optional(danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
danger = armyValueRequired.get_value_or(0);
|
||||
|
||||
if(allowGatherArmy && danger > 0)
|
||||
{
|
||||
//we need to get army in order to conquer that place
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Gather army for %s, value=%s", hero->name, std::to_string(danger));
|
||||
#endif
|
||||
result.push_back(sptr(Goals::GatherArmy(danger * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet) const
|
||||
{
|
||||
if(isBlockedBorderGate(firstTileToGet))
|
||||
{
|
||||
//FIXME: this way we'll not visit gate and activate quest :?
|
||||
return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->subID));
|
||||
}
|
||||
|
||||
auto topObj = cb->getTopObj(firstTileToGet);
|
||||
if(topObj)
|
||||
{
|
||||
|
||||
if(vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[hero], topObj))
|
||||
{
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
if(topObj->ID == Obj::HERO && cb->getPlayerRelations(hero->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
|
||||
{
|
||||
if(topObj != hero.get(true)) //the hero we want to free
|
||||
{
|
||||
logAi->error("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
|
||||
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
}
|
||||
|
||||
if(topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
|
||||
{
|
||||
if(shouldVisit(hero, topObj))
|
||||
{
|
||||
//do NOT use VISIT_TILE, as tile with quets guard can't be visited
|
||||
return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero));
|
||||
}
|
||||
|
||||
auto questObj = dynamic_cast<const IQuestObject*>(topObj);
|
||||
|
||||
if(questObj)
|
||||
{
|
||||
auto questInfo = QuestInfo(questObj->quest, topObj, topObj->visitablePos());
|
||||
|
||||
return sptr(Goals::CompleteQuest(questInfo));
|
||||
}
|
||||
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
}
|
||||
|
||||
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
|
||||
}
|
||||
|
||||
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes)
|
||||
{
|
||||
logAi->debug("AIPathfinder has been reseted.");
|
||||
pathfinder->updatePaths(heroes);
|
||||
}
|
||||
|
||||
void PathfindingManager::updatePaths(const HeroPtr & hero)
|
||||
{
|
||||
pathfinder->updatePaths(hero);
|
||||
}
|
70
AI/Nullkiller/Pathfinding/PathfindingManager.h
Normal file
70
AI/Nullkiller/Pathfinding/PathfindingManager.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* PathfindingManager.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 "../VCAI.h"
|
||||
#include "AINodeStorage.h"
|
||||
|
||||
class DLL_EXPORT IPathfindingManager
|
||||
{
|
||||
public:
|
||||
virtual ~IPathfindingManager() = default;
|
||||
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
||||
virtual void setAI(VCAI * AI) = 0;
|
||||
|
||||
virtual void updatePaths(std::vector<HeroPtr> heroes) = 0;
|
||||
virtual void updatePaths(const HeroPtr & hero) = 0;
|
||||
virtual Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const = 0;
|
||||
virtual Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
|
||||
virtual Goals::TGoalVec howToVisitTile(const int3 & tile) const = 0;
|
||||
virtual Goals::TGoalVec howToVisitObj(ObjectIdRef obj) const = 0;
|
||||
virtual std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT PathfindingManager : public IPathfindingManager
|
||||
{
|
||||
friend class AIhelper;
|
||||
|
||||
private:
|
||||
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
|
||||
VCAI * ai;
|
||||
std::unique_ptr<AIPathfinder> pathfinder;
|
||||
|
||||
public:
|
||||
PathfindingManager() = default;
|
||||
PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
|
||||
|
||||
Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitTile(const int3 & tile) const override;
|
||||
Goals::TGoalVec howToVisitObj(ObjectIdRef obj) const override;
|
||||
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes) override;
|
||||
void updatePaths(const HeroPtr & hero) override;
|
||||
|
||||
STRONG_INLINE
|
||||
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
return pathfinder->isTileAccessible(hero, tile);
|
||||
}
|
||||
|
||||
private:
|
||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
|
||||
Goals::TGoalVec findPath(
|
||||
HeroPtr hero,
|
||||
crint3 dest,
|
||||
bool allowGatherArmy,
|
||||
const std::function<Goals::TSubgoal(int3)> goalFactory) const;
|
||||
|
||||
Goals::TSubgoal clearWayTo(HeroPtr hero, int3 firstTileToGet) const;
|
||||
};
|
154
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp
Normal file
154
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* AILayerTransitionRule.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 "AILayerTransitionRule.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
:cb(cb), ai(ai), nodeStorage(nodeStorage)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
||||
void AILayerTransitionRule::process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const
|
||||
{
|
||||
LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper);
|
||||
|
||||
if(!destination.blocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
|
||||
{
|
||||
std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
|
||||
|
||||
if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AILayerTransitionRule::setup()
|
||||
{
|
||||
std::vector<const IShipyard *> shipyards;
|
||||
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
if(t->hasBuilt(BuildingID::SHIPYARD))
|
||||
shipyards.push_back(t);
|
||||
}
|
||||
|
||||
for(const CGObjectInstance * obj : ai->visitableObjs)
|
||||
{
|
||||
if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
|
||||
{
|
||||
if(const IShipyard * shipyard = IShipyard::castFrom(obj))
|
||||
shipyards.push_back(shipyard);
|
||||
}
|
||||
}
|
||||
|
||||
for(const IShipyard * shipyard : shipyards)
|
||||
{
|
||||
if(shipyard->shipyardStatus() == IShipyard::GOOD)
|
||||
{
|
||||
int3 boatLocation = shipyard->bestLocation();
|
||||
virtualBoats[boatLocation] = std::make_shared<BuildBoatAction>(shipyard);
|
||||
logAi->debug("Virtual boat added at %s", boatLocation.toString());
|
||||
}
|
||||
}
|
||||
|
||||
auto hero = nodeStorage->getHero();
|
||||
auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
|
||||
|
||||
if(hero->canCastThisSpell(summonBoatSpell)
|
||||
&& hero->getSpellSchoolLevel(summonBoatSpell) >= SecSkillLevel::ADVANCED)
|
||||
{
|
||||
// TODO: For lower school level we might need to check the existance of some boat
|
||||
summonableVirtualBoat.reset(new SummonBoatAction());
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const VirtualBoatAction> AILayerTransitionRule::findVirtualBoat(
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source) const
|
||||
{
|
||||
std::shared_ptr<const VirtualBoatAction> virtualBoat;
|
||||
|
||||
if(vstd::contains(virtualBoats, destination.coord))
|
||||
{
|
||||
virtualBoat = virtualBoats.at(destination.coord);
|
||||
}
|
||||
else if(
|
||||
summonableVirtualBoat
|
||||
&& summonableVirtualBoat->isAffordableBy(nodeStorage->getHero(), nodeStorage->getAINode(source.node)))
|
||||
{
|
||||
virtualBoat = summonableVirtualBoat;
|
||||
}
|
||||
|
||||
return virtualBoat;
|
||||
}
|
||||
|
||||
bool AILayerTransitionRule::tryEmbarkVirtualBoat(
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
std::shared_ptr<const VirtualBoatAction> virtualBoat) const
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
|
||||
{
|
||||
auto boatNodeOptional = nodeStorage->getOrCreateNode(
|
||||
node->coord,
|
||||
node->layer,
|
||||
node->chainMask | virtualBoat->getSpecialChain());
|
||||
|
||||
if(boatNodeOptional)
|
||||
{
|
||||
AIPathNode * boatNode = boatNodeOptional.get();
|
||||
|
||||
if(boatNode->action == CGPathNode::UNKNOWN)
|
||||
{
|
||||
boatNode->specialAction = virtualBoat;
|
||||
destination.blocked = false;
|
||||
destination.action = CGPathNode::ENodeAction::EMBARK;
|
||||
destination.node = boatNode;
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Special transition node already allocated. Blocked moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logAi->debug(
|
||||
"Can not allocate special transition node while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
52
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h
Normal file
52
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* AILayerTransitionRule.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 "../AINodeStorage.h"
|
||||
#include "../../VCAI.h"
|
||||
#include "../Actions/BoatActions.h"
|
||||
#include "../../../../CCallback.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class AILayerTransitionRule : public LayerTransitionRule
|
||||
{
|
||||
private:
|
||||
CPlayerSpecificInfoCallback * cb;
|
||||
VCAI * ai;
|
||||
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
|
||||
|
||||
public:
|
||||
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
|
||||
virtual void process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const override;
|
||||
|
||||
private:
|
||||
void setup();
|
||||
|
||||
std::shared_ptr<const VirtualBoatAction> findVirtualBoat(
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source) const;
|
||||
|
||||
bool tryEmbarkVirtualBoat(
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
std::shared_ptr<const VirtualBoatAction> virtualBoat) const;
|
||||
};
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* AIMovementAfterDestinationRule.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 "AIMovementAfterDestinationRule.h"
|
||||
#include "../Actions/BattleAction.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
:cb(cb), nodeStorage(nodeStorage)
|
||||
{
|
||||
}
|
||||
|
||||
void AIMovementAfterDestinationRule::process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const
|
||||
{
|
||||
if(nodeStorage->hasBetterChain(source, destination))
|
||||
{
|
||||
destination.blocked = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
||||
|
||||
if(blocker == BlockingReason::NONE)
|
||||
return;
|
||||
|
||||
if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject)
|
||||
{
|
||||
auto objID = destination.nodeObject->ID;
|
||||
auto enemyHero = objID == Obj::HERO && destination.objectRelations == PlayerRelations::ENEMIES;
|
||||
|
||||
if(!enemyHero && !isObjectRemovable(destination.nodeObject))
|
||||
{
|
||||
destination.blocked = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(blocker == BlockingReason::DESTINATION_VISIT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(blocker == BlockingReason::DESTINATION_GUARDED)
|
||||
{
|
||||
auto srcGuardians = cb->getGuardingCreatures(source.coord);
|
||||
auto destGuardians = cb->getGuardingCreatures(destination.coord);
|
||||
|
||||
if(destGuardians.empty())
|
||||
{
|
||||
destination.blocked = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool
|
||||
{
|
||||
return vstd::contains(srcGuardians, destGuard);
|
||||
});
|
||||
|
||||
auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
|
||||
if(guardsAlreadyBypassed && nodeStorage->isBattleNode(source.node))
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Bypass guard at destination while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
|
||||
auto battleNodeOptional = nodeStorage->getOrCreateNode(
|
||||
destination.coord,
|
||||
destination.node->layer,
|
||||
destNode->chainMask | AINodeStorage::BATTLE_CHAIN);
|
||||
|
||||
if(!battleNodeOptional)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Can not allocate battle node while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
|
||||
destination.blocked = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AIPathNode * battleNode = battleNodeOptional.get();
|
||||
|
||||
if(battleNode->locked)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Block bypass guard at destination while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
destination.blocked = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto danger = nodeStorage->evaluateDanger(destination.coord);
|
||||
|
||||
destination.node = battleNode;
|
||||
nodeStorage->commit(destination, source);
|
||||
|
||||
if(battleNode->danger < danger)
|
||||
{
|
||||
battleNode->danger = danger;
|
||||
}
|
||||
|
||||
battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Begin bypass guard at destination with danger %s while moving %s -> %s",
|
||||
std::to_string(danger),
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
destination.blocked = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* AIMovementAfterDestinationRule.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 "../AINodeStorage.h"
|
||||
#include "../../VCAI.h"
|
||||
#include "../../../../CCallback.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class AIMovementAfterDestinationRule : public MovementAfterDestinationRule
|
||||
{
|
||||
private:
|
||||
CPlayerSpecificInfoCallback * cb;
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
|
||||
public:
|
||||
AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
|
||||
virtual void process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const override;
|
||||
};
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* AIMovementToDestinationRule.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 "AIMovementToDestinationRule.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AIMovementToDestinationRule::AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
: nodeStorage(nodeStorage)
|
||||
{
|
||||
}
|
||||
|
||||
void AIMovementToDestinationRule::process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const
|
||||
{
|
||||
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
||||
|
||||
if(blocker == BlockingReason::NONE)
|
||||
return;
|
||||
|
||||
if(blocker == BlockingReason::DESTINATION_BLOCKED
|
||||
&& destination.action == CGPathNode::EMBARK
|
||||
&& nodeStorage->getAINode(destination.node)->specialAction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node))
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Bypass src guard while moving from %s to %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
destination.blocked = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* AIMovementToDestinationRule.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 "../AINodeStorage.h"
|
||||
#include "../../VCAI.h"
|
||||
#include "../../../../CCallback.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class AIMovementToDestinationRule : public MovementToDestinationRule
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
|
||||
public:
|
||||
AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
|
||||
virtual void process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const override;
|
||||
};
|
||||
}
|
47
AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.cpp
Normal file
47
AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* AIPreviousNodeRule.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 "AIPreviousNodeRule.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AIPreviousNodeRule::AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
: nodeStorage(nodeStorage)
|
||||
{
|
||||
}
|
||||
|
||||
void AIPreviousNodeRule::process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const
|
||||
{
|
||||
if(source.node->action == CGPathNode::ENodeAction::BLOCKING_VISIT || source.node->action == CGPathNode::ENodeAction::VISIT)
|
||||
{
|
||||
// we can not directly bypass objects, we need to interact with them first
|
||||
destination.node->theNodeBefore = source.node;
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Link src node %s to destination node %s while bypassing visitable obj",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
auto aiSourceNode = nodeStorage->getAINode(source.node);
|
||||
|
||||
if(aiSourceNode->specialAction)
|
||||
{
|
||||
// there is some action on source tile which should be performed before we can bypass it
|
||||
destination.node->theNodeBefore = source.node;
|
||||
}
|
||||
}
|
||||
}
|
35
AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.h
Normal file
35
AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* AIPreviousNodeRule.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 "../AINodeStorage.h"
|
||||
#include "../../VCAI.h"
|
||||
#include "../../../../CCallback.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class AIPreviousNodeRule : public MovementToDestinationRule
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
|
||||
public:
|
||||
AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
|
||||
virtual void process(
|
||||
const PathNodeInfo & source,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
CPathfinderHelper * pathfinderHelper) const override;
|
||||
};
|
||||
}
|
364
AI/Nullkiller/ResourceManager.cpp
Normal file
364
AI/Nullkiller/ResourceManager.cpp
Normal file
@ -0,0 +1,364 @@
|
||||
/*
|
||||
* ResourceManager.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "Goals/Goals.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
#define GOLD_RESERVE (10000); //at least we'll be able to reach capitol
|
||||
|
||||
ResourceObjective::ResourceObjective(const TResources & Res, Goals::TSubgoal Goal)
|
||||
: resources(Res), goal(Goal)
|
||||
{
|
||||
}
|
||||
|
||||
bool ResourceObjective::operator<(const ResourceObjective & ro) const
|
||||
{
|
||||
return goal->priority < ro.goal->priority;
|
||||
}
|
||||
|
||||
ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI)
|
||||
: ai(AI), cb(CB)
|
||||
{
|
||||
}
|
||||
|
||||
void ResourceManager::init(CPlayerSpecificInfoCallback * CB)
|
||||
{
|
||||
cb = CB;
|
||||
}
|
||||
|
||||
void ResourceManager::setAI(VCAI * AI)
|
||||
{
|
||||
ai = AI;
|
||||
}
|
||||
|
||||
bool ResourceManager::canAfford(const TResources & cost) const
|
||||
{
|
||||
return freeResources().canAfford(cost);
|
||||
}
|
||||
|
||||
TResources ResourceManager::estimateIncome() const
|
||||
{
|
||||
TResources ret;
|
||||
for (const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
ret += t->dailyIncome();
|
||||
}
|
||||
|
||||
for (const CGObjectInstance * obj : ai->getFlaggedObjects())
|
||||
{
|
||||
if (obj->ID == Obj::MINE)
|
||||
{
|
||||
switch (obj->subID)
|
||||
{
|
||||
case Res::WOOD:
|
||||
case Res::ORE:
|
||||
ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION;
|
||||
break;
|
||||
case Res::GOLD:
|
||||
case 7: //abandoned mine -> also gold
|
||||
ret[Res::GOLD] += GOLD_MINE_PRODUCTION;
|
||||
break;
|
||||
default:
|
||||
ret[obj->subID] += RESOURCE_MINE_PRODUCTION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ResourceManager::reserveResoures(const TResources & res, Goals::TSubgoal goal)
|
||||
{
|
||||
if (!goal->invalid())
|
||||
tryPush(ResourceObjective(res, goal));
|
||||
else
|
||||
logAi->warn("Attempt to reserve resources for Invalid goal");
|
||||
}
|
||||
|
||||
Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o) const
|
||||
{
|
||||
auto allResources = cb->getResourceAmount();
|
||||
auto income = estimateIncome();
|
||||
Res::ERes resourceType = Res::INVALID;
|
||||
TResource amountToCollect = 0;
|
||||
|
||||
typedef std::pair<Res::ERes, TResource> resPair;
|
||||
std::map<Res::ERes, TResource> missingResources;
|
||||
|
||||
//TODO: unit test for complex resource sets
|
||||
|
||||
//sum missing resources of given type for ALL reserved objectives
|
||||
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
|
||||
{
|
||||
//choose specific resources we need for this goal (not 0)
|
||||
for (auto r = Res::ResourceSet::nziterator(o.resources); r.valid(); r++)
|
||||
missingResources[r->resType] += it->resources[r->resType]; //goal it costs r units of resType
|
||||
}
|
||||
for (auto it = Res::ResourceSet::nziterator(o.resources); it.valid(); it++)
|
||||
{
|
||||
missingResources[it->resType] -= allResources[it->resType]; //missing = (what we need) - (what we have)
|
||||
vstd::amax(missingResources[it->resType], 0); // if we have more resources than reserved, we don't need them
|
||||
}
|
||||
vstd::erase_if(missingResources, [=](const resPair & p) -> bool
|
||||
{
|
||||
return !(p.second); //in case evaluated to 0 or less
|
||||
});
|
||||
if (missingResources.empty()) //FIXME: should be unit-tested out
|
||||
{
|
||||
logAi->error("We don't need to collect resources %s for goal %s", o.resources.toString(), o.goal->name());
|
||||
return o.goal;
|
||||
}
|
||||
|
||||
float goalPriority = 10; //arbitrary, will be divided
|
||||
for (const resPair & p : missingResources)
|
||||
{
|
||||
if (!income[p.first]) //prioritize resources with 0 income
|
||||
{
|
||||
resourceType = p.first;
|
||||
amountToCollect = p.second;
|
||||
goalPriority /= amountToCollect; //need more resources -> lower priority
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (resourceType == Res::INVALID) //no needed resources has 0 income,
|
||||
{
|
||||
//find the one which takes longest to collect
|
||||
typedef std::pair<Res::ERes, float> timePair;
|
||||
std::map<Res::ERes, float> daysToEarn;
|
||||
for (auto it : missingResources)
|
||||
daysToEarn[it.first] = (float)missingResources[it.first] / income[it.first];
|
||||
auto incomeComparer = [&income](const timePair & lhs, const timePair & rhs) -> bool
|
||||
{
|
||||
//theoretically income can be negative, but that falls into this comparison
|
||||
return lhs.second < rhs.second;
|
||||
};
|
||||
|
||||
resourceType = boost::max_element(daysToEarn, incomeComparer)->first;
|
||||
amountToCollect = missingResources[resourceType];
|
||||
goalPriority /= daysToEarn[resourceType]; //more days - lower priority
|
||||
}
|
||||
if (resourceType == Res::GOLD)
|
||||
goalPriority *= 1000;
|
||||
|
||||
//this is abstract goal and might take soem time to complete
|
||||
return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect).setisAbstract(true));
|
||||
}
|
||||
|
||||
Goals::TSubgoal ResourceManager::whatToDo() const //suggest any goal
|
||||
{
|
||||
if (queue.size())
|
||||
{
|
||||
auto o = queue.top();
|
||||
|
||||
auto allResources = cb->getResourceAmount(); //we don't consider savings, it's out top-priority goal
|
||||
if (allResources.canAfford(o.resources))
|
||||
return o.goal;
|
||||
else //we can't afford even top-priority goal, need to collect resources
|
||||
return collectResourcesForOurGoal(o);
|
||||
}
|
||||
else
|
||||
return Goals::sptr(Goals::Invalid()); //nothing else to do
|
||||
}
|
||||
|
||||
Goals::TSubgoal ResourceManager::whatToDo(TResources &res, Goals::TSubgoal goal)
|
||||
{
|
||||
logAi->trace("ResourceManager: checking goal %s which requires resources %s", goal->name(), res.toString());
|
||||
|
||||
TResources accumulatedResources;
|
||||
auto allResources = cb->getResourceAmount();
|
||||
|
||||
ResourceObjective ro(res, goal);
|
||||
tryPush(ro);
|
||||
//check if we can afford all the objectives with higher priority first
|
||||
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
|
||||
{
|
||||
accumulatedResources += it->resources;
|
||||
|
||||
logAi->trace(
|
||||
"ResourceManager: checking goal %s, accumulatedResources=%s, available=%s",
|
||||
it->goal->name(),
|
||||
accumulatedResources.toString(),
|
||||
allResources.toString());
|
||||
|
||||
if(!accumulatedResources.canBeAfforded(allResources))
|
||||
{
|
||||
//can't afford
|
||||
break;
|
||||
}
|
||||
else //can afford all goals up to this point
|
||||
{
|
||||
if(it->goal == goal)
|
||||
{
|
||||
logAi->debug("ResourceManager: can afford goal %s", goal->name());
|
||||
return goal; //can afford immediately
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logAi->debug("ResourceManager: can not afford goal %s", goal->name());
|
||||
|
||||
return collectResourcesForOurGoal(ro);
|
||||
}
|
||||
|
||||
bool ResourceManager::containsObjective(Goals::TSubgoal goal) const
|
||||
{
|
||||
logAi->trace("Entering ResourceManager.containsObjective goal=%s", goal->name());
|
||||
dumpToLog();
|
||||
|
||||
//TODO: unit tests for once
|
||||
for (auto objective : queue)
|
||||
{
|
||||
if (objective.goal == goal)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal)
|
||||
{
|
||||
logAi->trace("Entering ResourceManager.notifyGoalCompleted goal=%s", goal->name());
|
||||
|
||||
if (goal->invalid())
|
||||
logAi->warn("Attempt to complete Invalid goal");
|
||||
|
||||
std::function<bool(const Goals::TSubgoal &)> equivalentGoalsCheck = [goal](const Goals::TSubgoal & x) -> bool
|
||||
{
|
||||
return x == goal || x->fulfillsMe(goal);
|
||||
};
|
||||
|
||||
bool removedGoal = removeOutdatedObjectives(equivalentGoalsCheck);
|
||||
|
||||
dumpToLog();
|
||||
|
||||
return removedGoal;
|
||||
}
|
||||
|
||||
bool ResourceManager::updateGoal(Goals::TSubgoal goal)
|
||||
{
|
||||
//we update priority of goal if it is easier or more difficult to complete
|
||||
if (goal->invalid())
|
||||
logAi->warn("Attempt to update Invalid goal");
|
||||
|
||||
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
|
||||
{
|
||||
return ro.goal == goal;
|
||||
});
|
||||
if (it != queue.end())
|
||||
{
|
||||
it->goal->setpriority(goal->priority);
|
||||
auto handle = queue.s_handle_from_iterator(it);
|
||||
queue.update(handle); //restore order
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResourceManager::dumpToLog() const
|
||||
{
|
||||
for(auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
|
||||
{
|
||||
logAi->trace("ResourceManager contains goal %s which requires resources %s", it->goal->name(), it->resources.toString());
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::tryPush(const ResourceObjective & o)
|
||||
{
|
||||
auto goal = o.goal;
|
||||
|
||||
logAi->trace("ResourceManager: Trying to add goal %s which requires resources %s", goal->name(), o.resources.toString());
|
||||
dumpToLog();
|
||||
|
||||
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
|
||||
{
|
||||
return ro.goal == goal;
|
||||
});
|
||||
if (it != queue.end())
|
||||
{
|
||||
auto handle = queue.s_handle_from_iterator(it);
|
||||
vstd::amax(goal->priority, it->goal->priority); //increase priority if case
|
||||
//update resources with new value
|
||||
queue.update(handle, ResourceObjective(o.resources, goal)); //restore order
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue.push(o); //add new objective
|
||||
logAi->debug("Reserved resources (%s) for %s", o.resources.toString(), goal->name());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::hasTasksLeft() const
|
||||
{
|
||||
return !queue.empty();
|
||||
}
|
||||
|
||||
bool ResourceManager::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate)
|
||||
{
|
||||
bool removedAnything = false;
|
||||
while(true)
|
||||
{ //unfortunately we can't use remove_if on heap
|
||||
auto it = boost::find_if(queue, [&](const ResourceObjective & ro) -> bool
|
||||
{
|
||||
return predicate(ro.goal);
|
||||
});
|
||||
|
||||
if(it != queue.end()) //removed at least one
|
||||
{
|
||||
logAi->debug("Removing goal %s from ResourceManager.", it->goal->name());
|
||||
queue.erase(queue.s_handle_from_iterator(it));
|
||||
removedAnything = true;
|
||||
}
|
||||
else
|
||||
{ //found nothing more to remove
|
||||
break;
|
||||
}
|
||||
}
|
||||
return removedAnything;
|
||||
}
|
||||
|
||||
TResources ResourceManager::reservedResources() const
|
||||
{
|
||||
TResources res;
|
||||
for (auto it : queue) //substract the value of reserved goals
|
||||
res += it.resources;
|
||||
return res;
|
||||
}
|
||||
|
||||
TResources ResourceManager::freeResources() const
|
||||
{
|
||||
TResources myRes = cb->getResourceAmount();
|
||||
myRes -= reservedResources(); //substract the value of reserved goals
|
||||
|
||||
for (auto & val : myRes)
|
||||
vstd::amax(val, 0); //never negative
|
||||
|
||||
return myRes;
|
||||
}
|
||||
|
||||
TResource ResourceManager::freeGold() const
|
||||
{
|
||||
return freeResources()[Res::GOLD];
|
||||
}
|
||||
|
||||
TResources ResourceManager::allResources() const
|
||||
{
|
||||
return cb->getResourceAmount();
|
||||
}
|
||||
|
||||
TResource ResourceManager::allGold() const
|
||||
{
|
||||
return cb->getResourceAmount()[Res::GOLD];
|
||||
}
|
113
AI/Nullkiller/ResourceManager.h
Normal file
113
AI/Nullkiller/ResourceManager.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* ResourceManager.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "AIUtility.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "VCAI.h"
|
||||
#include <boost/heap/binomial_heap.hpp>
|
||||
|
||||
class AIhelper;
|
||||
class IResourceManager;
|
||||
|
||||
struct DLL_EXPORT ResourceObjective
|
||||
{
|
||||
ResourceObjective() = default;
|
||||
ResourceObjective(const TResources &res, Goals::TSubgoal goal);
|
||||
bool operator < (const ResourceObjective &ro) const;
|
||||
|
||||
TResources resources; //how many resoures do we need
|
||||
Goals::TSubgoal goal; //what for (build, gather army etc...)
|
||||
|
||||
//TODO: register?
|
||||
template<typename Handler> void serializeInternal(Handler & h, const int version)
|
||||
{
|
||||
h & resources;
|
||||
//h & goal; //FIXME: goal serialization is broken
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_EXPORT IResourceManager //: public: IAbstractManager
|
||||
{
|
||||
public:
|
||||
virtual ~IResourceManager() = default;
|
||||
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
||||
virtual void setAI(VCAI * AI) = 0;
|
||||
|
||||
virtual TResources reservedResources() const = 0;
|
||||
virtual TResources freeResources() const = 0;
|
||||
virtual TResource freeGold() const = 0;
|
||||
virtual TResources allResources() const = 0;
|
||||
virtual TResource allGold() const = 0;
|
||||
|
||||
virtual Goals::TSubgoal whatToDo() const = 0;//get highest-priority goal
|
||||
virtual Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) = 0;
|
||||
virtual bool containsObjective(Goals::TSubgoal goal) const = 0;
|
||||
virtual bool hasTasksLeft() const = 0;
|
||||
virtual bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) = 0; //remove ResourceObjectives from queue if ResourceObjective->goal meets specific criteria
|
||||
virtual bool notifyGoalCompleted(Goals::TSubgoal goal) = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT ResourceManager : public IResourceManager
|
||||
{
|
||||
/*Resource Manager is a smart shopping list for AI to help
|
||||
Uses priority queue based on CGoal.priority */
|
||||
friend class VCAI;
|
||||
friend class AIhelper;
|
||||
friend struct SetGlobalState;
|
||||
|
||||
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
|
||||
VCAI * ai;
|
||||
|
||||
public:
|
||||
ResourceManager() = default;
|
||||
ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
|
||||
|
||||
bool canAfford(const TResources & cost) const;
|
||||
TResources reservedResources() const override;
|
||||
TResources freeResources() const override;
|
||||
TResource freeGold() const override;
|
||||
TResources allResources() const override;
|
||||
TResource allGold() const override;
|
||||
|
||||
Goals::TSubgoal whatToDo() const override; //peek highest-priority goal
|
||||
Goals::TSubgoal whatToDo(TResources & res, Goals::TSubgoal goal) override; //can we afford this goal or need to CollectRes?
|
||||
bool containsObjective(Goals::TSubgoal goal) const override;
|
||||
bool hasTasksLeft() const override;
|
||||
bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
|
||||
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
|
||||
|
||||
protected: //not-const actions only for AI
|
||||
virtual void reserveResoures(const TResources & res, Goals::TSubgoal goal = Goals::TSubgoal());
|
||||
virtual bool updateGoal(Goals::TSubgoal goal); //new goal must have same properties but different priority
|
||||
virtual bool tryPush(const ResourceObjective &o);
|
||||
|
||||
//inner processing
|
||||
virtual TResources estimateIncome() const;
|
||||
virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const;
|
||||
|
||||
void init(CPlayerSpecificInfoCallback * CB) override;
|
||||
void setAI(VCAI * AI) override;
|
||||
|
||||
private:
|
||||
TResources saving;
|
||||
|
||||
boost::heap::binomial_heap<ResourceObjective> queue;
|
||||
|
||||
void dumpToLog() const;
|
||||
|
||||
//TODO: register?
|
||||
template<typename Handler> void serializeInternal(Handler & h, const int version)
|
||||
{
|
||||
h & saving;
|
||||
h & queue;
|
||||
}
|
||||
};
|
431
AI/Nullkiller/SectorMap.cpp
Normal file
431
AI/Nullkiller/SectorMap.cpp
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* SectorMap.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 "SectorMap.h"
|
||||
#include "VCAI.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../lib/CPathfinder.h"
|
||||
#include "../../lib/CGameState.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
|
||||
SectorMap::SectorMap()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
SectorMap::SectorMap(HeroPtr h)
|
||||
{
|
||||
update();
|
||||
makeParentBFS(h->visitablePos());
|
||||
}
|
||||
|
||||
bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t)
|
||||
{
|
||||
if (t->blocked && !t->visitable)
|
||||
{
|
||||
sec = NOT_AVAILABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos)
|
||||
{
|
||||
return markIfBlocked(sec, pos, getTile(pos));
|
||||
}
|
||||
|
||||
void SectorMap::update()
|
||||
{
|
||||
visibleTiles = cb->getAllVisibleTiles();
|
||||
auto shape = visibleTiles->shape();
|
||||
sector.resize(boost::extents[shape[0]][shape[1]][shape[2]]);
|
||||
|
||||
clear();
|
||||
int curSector = 3; //0 is invisible, 1 is not explored
|
||||
|
||||
CCallback * cbp = cb.get(); //optimization
|
||||
foreach_tile_pos([&](crint3 pos)
|
||||
{
|
||||
if (retrieveTile(pos) == NOT_CHECKED)
|
||||
{
|
||||
if (!markIfBlocked(retrieveTile(pos), pos))
|
||||
exploreNewSector(pos, curSector++, cbp);
|
||||
}
|
||||
});
|
||||
valid = true;
|
||||
}
|
||||
|
||||
SectorMap::TSectorID & SectorMap::retrieveTileN(SectorMap::TSectorArray & a, const int3 & pos)
|
||||
{
|
||||
return a[pos.x][pos.y][pos.z];
|
||||
}
|
||||
|
||||
const SectorMap::TSectorID & SectorMap::retrieveTileN(const SectorMap::TSectorArray & a, const int3 & pos)
|
||||
{
|
||||
return a[pos.x][pos.y][pos.z];
|
||||
}
|
||||
|
||||
void SectorMap::clear()
|
||||
{
|
||||
//TODO: rotate to [z][x][y]
|
||||
auto fow = cb->getVisibilityMap();
|
||||
//TODO: any magic to automate this? will need array->array conversion
|
||||
//std::transform(fow.begin(), fow.end(), sector.begin(), [](const ui8 &f) -> unsigned short
|
||||
//{
|
||||
// return f; //type conversion
|
||||
//});
|
||||
auto width = fow.size();
|
||||
auto height = fow.front().size();
|
||||
auto depth = fow.front().front().size();
|
||||
for (size_t x = 0; x < width; x++)
|
||||
{
|
||||
for (size_t y = 0; y < height; y++)
|
||||
{
|
||||
for (size_t z = 0; z < depth; z++)
|
||||
sector[x][y][z] = fow[x][y][z];
|
||||
}
|
||||
}
|
||||
valid = false;
|
||||
}
|
||||
|
||||
void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
|
||||
{
|
||||
Sector & s = infoOnSectors[num];
|
||||
s.id = num;
|
||||
s.water = getTile(pos)->isWater();
|
||||
|
||||
std::queue<int3> toVisit;
|
||||
toVisit.push(pos);
|
||||
while (!toVisit.empty())
|
||||
{
|
||||
int3 curPos = toVisit.front();
|
||||
toVisit.pop();
|
||||
TSectorID & sec = retrieveTile(curPos);
|
||||
if (sec == NOT_CHECKED)
|
||||
{
|
||||
const TerrainTile * t = getTile(curPos);
|
||||
if (!markIfBlocked(sec, curPos, t))
|
||||
{
|
||||
if (t->isWater() == s.water) //sector is only-water or only-land
|
||||
{
|
||||
sec = num;
|
||||
s.tiles.push_back(curPos);
|
||||
foreach_neighbour(cbp, curPos, [&](CCallback * cbp, crint3 neighPos)
|
||||
{
|
||||
if (retrieveTile(neighPos) == NOT_CHECKED)
|
||||
{
|
||||
toVisit.push(neighPos);
|
||||
//parent[neighPos] = curPos;
|
||||
}
|
||||
const TerrainTile * nt = getTile(neighPos);
|
||||
if (nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water))
|
||||
{
|
||||
s.embarkmentPoints.push_back(neighPos);
|
||||
}
|
||||
});
|
||||
|
||||
if (t->visitable)
|
||||
{
|
||||
auto obj = t->visitableObjects.front();
|
||||
if (cb->getObj(obj->id, false)) // FIXME: we have to filter invisible objcts like events, but probably TerrainTile shouldn't be used in SectorMap at all
|
||||
s.visitableObjs.push_back(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vstd::removeDuplicates(s.embarkmentPoints);
|
||||
}
|
||||
|
||||
void SectorMap::write(crstring fname)
|
||||
{
|
||||
std::ofstream out(fname);
|
||||
for (int k = 0; k < cb->getMapSize().z; k++)
|
||||
{
|
||||
for (int j = 0; j < cb->getMapSize().y; j++)
|
||||
{
|
||||
for (int i = 0; i < cb->getMapSize().x; i++)
|
||||
{
|
||||
out << (int)sector[i][j][k] << '\t';
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
this functions returns one target tile or invalid tile. We will use it to poll possible destinations
|
||||
For ship construction etc, another function (goal?) is needed
|
||||
*/
|
||||
int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
|
||||
{
|
||||
int3 ret(-1, -1, -1);
|
||||
|
||||
int sourceSector = retrieveTile(h->visitablePos());
|
||||
int destinationSector = retrieveTile(dst);
|
||||
|
||||
const Sector * src = &infoOnSectors[sourceSector];
|
||||
const Sector * dest = &infoOnSectors[destinationSector];
|
||||
|
||||
if (sourceSector != destinationSector) //use ships, shipyards etc..
|
||||
{
|
||||
if (ai->isAccessibleForHero(dst, h)) //pathfinder can find a way using ships and gates if tile is not blocked by objects
|
||||
return dst;
|
||||
|
||||
std::map<const Sector *, const Sector *> preds;
|
||||
std::queue<const Sector *> sectorQueue;
|
||||
sectorQueue.push(src);
|
||||
while (!sectorQueue.empty())
|
||||
{
|
||||
const Sector * s = sectorQueue.front();
|
||||
sectorQueue.pop();
|
||||
|
||||
for (int3 ep : s->embarkmentPoints)
|
||||
{
|
||||
Sector * neigh = &infoOnSectors[retrieveTile(ep)];
|
||||
//preds[s].push_back(neigh);
|
||||
if (!preds[neigh])
|
||||
{
|
||||
preds[neigh] = s;
|
||||
sectorQueue.push(neigh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!preds[dest])
|
||||
{
|
||||
//write("test.txt");
|
||||
|
||||
return ret;
|
||||
//throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
|
||||
}
|
||||
|
||||
std::vector<const Sector *> toTraverse;
|
||||
toTraverse.push_back(dest);
|
||||
while (toTraverse.back() != src)
|
||||
{
|
||||
toTraverse.push_back(preds[toTraverse.back()]);
|
||||
}
|
||||
|
||||
if (preds[dest])
|
||||
{
|
||||
//TODO: would be nice to find sectors in loop
|
||||
const Sector * sectorToReach = toTraverse.at(toTraverse.size() - 2);
|
||||
|
||||
if (!src->water && sectorToReach->water) //embark
|
||||
{
|
||||
//embark on ship -> look for an EP with a boat
|
||||
auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool
|
||||
{
|
||||
const TerrainTile * t = getTile(pos);
|
||||
if (t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
|
||||
{
|
||||
if (retrieveTile(pos) == sectorToReach->id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (firstEP != src->embarkmentPoints.end())
|
||||
{
|
||||
return *firstEP;
|
||||
}
|
||||
else
|
||||
{
|
||||
//we need to find a shipyard with an access to the desired sector's EP
|
||||
//TODO what about Summon Boat spell?
|
||||
std::vector<const IShipyard *> shipyards;
|
||||
for (const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
if (t->hasBuilt(BuildingID::SHIPYARD))
|
||||
shipyards.push_back(t);
|
||||
}
|
||||
|
||||
for (const CGObjectInstance * obj : ai->getFlaggedObjects())
|
||||
{
|
||||
if (obj->ID != Obj::TOWN) //towns were handled in the previous loop
|
||||
{
|
||||
if (const IShipyard * shipyard = IShipyard::castFrom(obj))
|
||||
shipyards.push_back(shipyard);
|
||||
}
|
||||
}
|
||||
|
||||
shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool
|
||||
{
|
||||
return shipyard->shipyardStatus() != 0 || retrieveTile(shipyard->bestLocation()) != sectorToReach->id;
|
||||
}), shipyards.end());
|
||||
|
||||
if (!shipyards.size())
|
||||
{
|
||||
//TODO consider possibility of building shipyard in a town
|
||||
return ret;
|
||||
|
||||
//throw cannotFulfillGoalException("There is no known shipyard!");
|
||||
}
|
||||
|
||||
//we have only shipyards that possibly can build ships onto the appropriate EP
|
||||
auto ownedGoodShipyard = boost::find_if(shipyards, [](const IShipyard * s) -> bool
|
||||
{
|
||||
return s->o->tempOwner == ai->playerID;
|
||||
});
|
||||
|
||||
if (ownedGoodShipyard != shipyards.end())
|
||||
{
|
||||
const IShipyard * s = *ownedGoodShipyard;
|
||||
TResources shipCost;
|
||||
s->getBoatCost(shipCost);
|
||||
if (cb->getResourceAmount().canAfford(shipCost))
|
||||
{
|
||||
int3 ret = s->bestLocation();
|
||||
cb->buildBoat(s); //TODO: move actions elsewhere
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO gather res
|
||||
return ret;
|
||||
|
||||
//throw cannotFulfillGoalException("Not enough resources to build a boat");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO pick best shipyard to take over
|
||||
return shipyards.front()->o->visitablePos();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (src->water && !sectorToReach->water)
|
||||
{
|
||||
//TODO
|
||||
//disembark
|
||||
return ret;
|
||||
}
|
||||
else //use subterranean gates - not needed since gates are now handled via Pathfinder
|
||||
{
|
||||
return ret;
|
||||
//throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ret;
|
||||
//throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?");
|
||||
}
|
||||
}
|
||||
else //tiles are in same sector
|
||||
{
|
||||
return findFirstVisitableTile(h, dst);
|
||||
}
|
||||
}
|
||||
|
||||
int3 SectorMap::findFirstVisitableTile(HeroPtr h, crint3 dst)
|
||||
{
|
||||
int3 ret(-1, -1, -1);
|
||||
int3 curtile = dst;
|
||||
|
||||
while (curtile != h->visitablePos())
|
||||
{
|
||||
auto topObj = cb->getTopObj(curtile);
|
||||
if (topObj && topObj->ID == Obj::HERO && topObj != h.h)
|
||||
{
|
||||
if (cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
|
||||
{
|
||||
logAi->warn("Another allied hero stands in our way");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable())
|
||||
{
|
||||
return curtile;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto i = parent.find(curtile);
|
||||
if (i != parent.end())
|
||||
{
|
||||
assert(curtile != i->second);
|
||||
curtile = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ret;
|
||||
//throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SectorMap::makeParentBFS(crint3 source)
|
||||
{
|
||||
parent.clear();
|
||||
|
||||
int mySector = retrieveTile(source);
|
||||
std::queue<int3> toVisit;
|
||||
toVisit.push(source);
|
||||
while (!toVisit.empty())
|
||||
{
|
||||
int3 curPos = toVisit.front();
|
||||
toVisit.pop();
|
||||
TSectorID & sec = retrieveTile(curPos);
|
||||
assert(sec == mySector); //consider only tiles from the same sector
|
||||
UNUSED(sec);
|
||||
|
||||
foreach_neighbour(curPos, [&](crint3 neighPos)
|
||||
{
|
||||
if (retrieveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
|
||||
{
|
||||
if (cb->canMoveBetween(curPos, neighPos))
|
||||
{
|
||||
toVisit.push(neighPos);
|
||||
parent[neighPos] = curPos;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SectorMap::TSectorID & SectorMap::retrieveTile(crint3 pos)
|
||||
{
|
||||
return retrieveTileN(sector, pos);
|
||||
}
|
||||
|
||||
TerrainTile * SectorMap::getTile(crint3 pos) const
|
||||
{
|
||||
//out of bounds access should be handled by boost::multi_array
|
||||
//still we cached this array to avoid any checks
|
||||
return visibleTiles->operator[](pos.x)[pos.y][pos.z];
|
||||
}
|
||||
|
||||
std::vector<const CGObjectInstance *> SectorMap::getNearbyObjs(HeroPtr h, bool sectorsAround)
|
||||
{
|
||||
const Sector * heroSector = &infoOnSectors[retrieveTile(h->visitablePos())];
|
||||
if (sectorsAround)
|
||||
{
|
||||
std::vector<const CGObjectInstance *> ret;
|
||||
for (auto embarkPoint : heroSector->embarkmentPoints)
|
||||
{
|
||||
const Sector * embarkSector = &infoOnSectors[retrieveTile(embarkPoint)];
|
||||
range::copy(embarkSector->visitableObjs, std::back_inserter(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return heroSector->visitableObjs;
|
||||
}
|
70
AI/Nullkiller/SectorMap.h
Normal file
70
AI/Nullkiller/SectorMap.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SectorMap.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"
|
||||
|
||||
enum
|
||||
{
|
||||
NOT_VISIBLE = 0,
|
||||
NOT_CHECKED = 1,
|
||||
NOT_AVAILABLE
|
||||
};
|
||||
|
||||
struct SectorMap
|
||||
{
|
||||
//a sector is set of tiles that would be mutually reachable if all visitable objs would be passable (incl monsters)
|
||||
struct Sector
|
||||
{
|
||||
int id;
|
||||
std::vector<int3> tiles;
|
||||
std::vector<int3> embarkmentPoints; //tiles of other sectors onto which we can (dis)embark
|
||||
std::vector<const CGObjectInstance *> visitableObjs;
|
||||
bool water; //all tiles of sector are land or water
|
||||
Sector()
|
||||
{
|
||||
id = -1;
|
||||
water = false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unsigned short TSectorID; //smaller than int to allow -1 value. Max number of sectors 65K should be enough for any proper map.
|
||||
typedef boost::multi_array<TSectorID, 3> TSectorArray;
|
||||
|
||||
bool valid; //some kind of lazy eval
|
||||
std::map<int3, int3> parent;
|
||||
TSectorArray sector;
|
||||
//std::vector<std::vector<std::vector<unsigned char>>> pathfinderSector;
|
||||
|
||||
std::map<int, Sector> infoOnSectors;
|
||||
std::shared_ptr<boost::multi_array<TerrainTile *, 3>> visibleTiles;
|
||||
|
||||
SectorMap();
|
||||
SectorMap(HeroPtr h);
|
||||
void update();
|
||||
void clear();
|
||||
void exploreNewSector(crint3 pos, int num, CCallback * cbp);
|
||||
void write(crstring fname);
|
||||
|
||||
bool markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t);
|
||||
bool markIfBlocked(TSectorID & sec, crint3 pos);
|
||||
TSectorID & retrieveTile(crint3 pos);
|
||||
TSectorID & retrieveTileN(TSectorArray & vectors, const int3 & pos);
|
||||
const TSectorID & retrieveTileN(const TSectorArray & vectors, const int3 & pos);
|
||||
TerrainTile * getTile(crint3 pos) const;
|
||||
std::vector<const CGObjectInstance *> getNearbyObjs(HeroPtr h, bool sectorsAround);
|
||||
|
||||
void makeParentBFS(crint3 source);
|
||||
|
||||
int3 firstTileToGet(HeroPtr h, crint3 dst); //if h wants to reach tile dst, which tile he should visit to clear the way?
|
||||
int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
|
||||
};
|
1
AI/Nullkiller/StdInc.cpp
Normal file
1
AI/Nullkiller/StdInc.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "StdInc.h"
|
2
AI/Nullkiller/StdInc.h
Normal file
2
AI/Nullkiller/StdInc.h
Normal file
@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include "../../Global.h"
|
184
AI/Nullkiller/VCAI.cbp
Normal file
184
AI/Nullkiller/VCAI.cbp
Normal file
@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="VCAI" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug-win32">
|
||||
<Option platforms="Windows;" />
|
||||
<Option output="../VCAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Debug/x86" />
|
||||
<Option type="3" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-Og" />
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add directory="../" />
|
||||
<Add directory="$(#boost.lib32)" />
|
||||
</Linker>
|
||||
</Target>
|
||||
<Target title="Release-win32">
|
||||
<Option platforms="Windows;" />
|
||||
<Option output="../VCAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Release/x86" />
|
||||
<Option type="3" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-fomit-frame-pointer" />
|
||||
<Add option="-O3" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
<Add directory="../" />
|
||||
<Add directory="$(#boost.lib32)" />
|
||||
</Linker>
|
||||
</Target>
|
||||
<Target title="Debug-win64">
|
||||
<Option platforms="Windows;" />
|
||||
<Option output="../VCAI" imp_lib="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).a" def_file="$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).def" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Debug/x64" />
|
||||
<Option type="3" />
|
||||
<Option compiler="gnu_gcc_compiler_x64" />
|
||||
<Compiler>
|
||||
<Add option="-Og" />
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add directory="../" />
|
||||
<Add directory="$(#boost.lib64)" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-pedantic" />
|
||||
<Add option="-Wextra" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="-std=gnu++11" />
|
||||
<Add option="-fexceptions" />
|
||||
<Add option="-Wpointer-arith" />
|
||||
<Add option="-Wno-switch" />
|
||||
<Add option="-Wno-sign-compare" />
|
||||
<Add option="-Wno-unused-parameter" />
|
||||
<Add option="-Wno-overloaded-virtual" />
|
||||
<Add option="-DBOOST_THREAD_USE_LIB" />
|
||||
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
|
||||
<Add option="-D_WIN32_WINNT=0x0501" />
|
||||
<Add option="-D_WIN32" />
|
||||
<Add option="-DFL_CPP11" />
|
||||
<Add directory="$(#boost.include)" />
|
||||
<Add directory="../../include" />
|
||||
<Add directory="../FuzzyLite/fuzzylite" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-lFuzzyLite" />
|
||||
<Add option="-lboost_system$(#boost.libsuffix)" />
|
||||
<Add option="-lboost_thread$(#boost.libsuffix)" />
|
||||
<Add option="-lboost_chrono$(#boost.libsuffix)" />
|
||||
<Add option="-lVCMI_lib" />
|
||||
<Add directory="../.." />
|
||||
</Linker>
|
||||
<Unit filename="AIUtility.cpp" />
|
||||
<Unit filename="AIUtility.h" />
|
||||
<Unit filename="AIhelper.cpp" />
|
||||
<Unit filename="AIhelper.h" />
|
||||
<Unit filename="BuildingManager.cpp" />
|
||||
<Unit filename="BuildingManager.h" />
|
||||
<Unit filename="FuzzyEngines.cpp" />
|
||||
<Unit filename="FuzzyEngines.h" />
|
||||
<Unit filename="FuzzyHelper.cpp" />
|
||||
<Unit filename="FuzzyHelper.h" />
|
||||
<Unit filename="Goals/AbstractGoal.cpp" />
|
||||
<Unit filename="Goals/AbstractGoal.h" />
|
||||
<Unit filename="Goals/AdventureSpellCast.cpp" />
|
||||
<Unit filename="Goals/AdventureSpellCast.h" />
|
||||
<Unit filename="Goals/Build.cpp" />
|
||||
<Unit filename="Goals/Build.h" />
|
||||
<Unit filename="Goals/BuildBoat.cpp" />
|
||||
<Unit filename="Goals/BuildBoat.h" />
|
||||
<Unit filename="Goals/BuildThis.cpp" />
|
||||
<Unit filename="Goals/BuildThis.h" />
|
||||
<Unit filename="Goals/BuyArmy.cpp" />
|
||||
<Unit filename="Goals/BuyArmy.h" />
|
||||
<Unit filename="Goals/CGoal.h" />
|
||||
<Unit filename="Goals/ClearWayTo.cpp" />
|
||||
<Unit filename="Goals/ClearWayTo.h" />
|
||||
<Unit filename="Goals/CollectRes.cpp" />
|
||||
<Unit filename="Goals/CollectRes.h" />
|
||||
<Unit filename="Goals/CompleteQuest.cpp" />
|
||||
<Unit filename="Goals/CompleteQuest.h" />
|
||||
<Unit filename="Goals/Conquer.cpp" />
|
||||
<Unit filename="Goals/Conquer.h" />
|
||||
<Unit filename="Goals/DigAtTile.cpp" />
|
||||
<Unit filename="Goals/DigAtTile.h" />
|
||||
<Unit filename="Goals/Explore.cpp" />
|
||||
<Unit filename="Goals/Explore.h" />
|
||||
<Unit filename="Goals/FindObj.cpp" />
|
||||
<Unit filename="Goals/FindObj.h" />
|
||||
<Unit filename="Goals/GatherArmy.cpp" />
|
||||
<Unit filename="Goals/GatherArmy.h" />
|
||||
<Unit filename="Goals/GatherTroops.cpp" />
|
||||
<Unit filename="Goals/GatherTroops.h" />
|
||||
<Unit filename="Goals/GetArtOfType.cpp" />
|
||||
<Unit filename="Goals/GetArtOfType.h" />
|
||||
<Unit filename="Goals/Goals.h" />
|
||||
<Unit filename="Goals/Invalid.h" />
|
||||
<Unit filename="Goals/RecruitHero.cpp" />
|
||||
<Unit filename="Goals/RecruitHero.h" />
|
||||
<Unit filename="Goals/Trade.cpp" />
|
||||
<Unit filename="Goals/Trade.h" />
|
||||
<Unit filename="Goals/VisitHero.cpp" />
|
||||
<Unit filename="Goals/VisitHero.h" />
|
||||
<Unit filename="Goals/VisitObj.cpp" />
|
||||
<Unit filename="Goals/VisitObj.h" />
|
||||
<Unit filename="Goals/VisitTile.cpp" />
|
||||
<Unit filename="Goals/VisitTile.h" />
|
||||
<Unit filename="Goals/Win.cpp" />
|
||||
<Unit filename="Goals/Win.h" />
|
||||
<Unit filename="MapObjectsEvaluator.cpp" />
|
||||
<Unit filename="MapObjectsEvaluator.h" />
|
||||
<Unit filename="Pathfinding/AINodeStorage.cpp" />
|
||||
<Unit filename="Pathfinding/AINodeStorage.h" />
|
||||
<Unit filename="Pathfinding/AIPathfinder.cpp" />
|
||||
<Unit filename="Pathfinding/AIPathfinder.h" />
|
||||
<Unit filename="Pathfinding/AIPathfinderConfig.cpp" />
|
||||
<Unit filename="Pathfinding/AIPathfinderConfig.h" />
|
||||
<Unit filename="Pathfinding/Actions/BattleAction.cpp" />
|
||||
<Unit filename="Pathfinding/Actions/BattleAction.h" />
|
||||
<Unit filename="Pathfinding/Actions/BoatActions.cpp" />
|
||||
<Unit filename="Pathfinding/Actions/BoatActions.h" />
|
||||
<Unit filename="Pathfinding/Actions/ISpecialAction.h" />
|
||||
<Unit filename="Pathfinding/Actions/TownPortalAction.cpp" />
|
||||
<Unit filename="Pathfinding/Actions/TownPortalAction.h" />
|
||||
<Unit filename="Pathfinding/PathfindingManager.cpp" />
|
||||
<Unit filename="Pathfinding/PathfindingManager.h" />
|
||||
<Unit filename="Pathfinding/Rules/AILayerTransitionRule.cpp" />
|
||||
<Unit filename="Pathfinding/Rules/AILayerTransitionRule.h" />
|
||||
<Unit filename="Pathfinding/Rules/AIMovementAfterDestinationRule.cpp" />
|
||||
<Unit filename="Pathfinding/Rules/AIMovementAfterDestinationRule.h" />
|
||||
<Unit filename="Pathfinding/Rules/AIMovementToDestinationRule.cpp" />
|
||||
<Unit filename="Pathfinding/Rules/AIMovementToDestinationRule.h" />
|
||||
<Unit filename="Pathfinding/Rules/AIPreviousNodeRule.cpp" />
|
||||
<Unit filename="Pathfinding/Rules/AIPreviousNodeRule.h" />
|
||||
<Unit filename="ResourceManager.cpp" />
|
||||
<Unit filename="ResourceManager.h" />
|
||||
<Unit filename="SectorMap.cpp" />
|
||||
<Unit filename="SectorMap.h" />
|
||||
<Unit filename="StdInc.h">
|
||||
<Option compile="1" />
|
||||
<Option weight="0" />
|
||||
</Unit>
|
||||
<Unit filename="VCAI.cpp" />
|
||||
<Unit filename="VCAI.h" />
|
||||
<Unit filename="main.cpp" />
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
<lib_finder disable_auto="1" />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
2929
AI/Nullkiller/VCAI.cpp
Normal file
2929
AI/Nullkiller/VCAI.cpp
Normal file
File diff suppressed because it is too large
Load Diff
404
AI/Nullkiller/VCAI.h
Normal file
404
AI/Nullkiller/VCAI.h
Normal file
@ -0,0 +1,404 @@
|
||||
/*
|
||||
* 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 "AIUtility.h"
|
||||
#include "Goals/AbstractGoal.h"
|
||||
#include "../../lib/AI_Base.h"
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/CThreadHelper.h"
|
||||
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/mapObjects/MiscObjects.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "Pathfinding/AIPathfinder.h"
|
||||
|
||||
struct QuestInfo;
|
||||
|
||||
class AIhelper;
|
||||
|
||||
class AIStatus
|
||||
{
|
||||
boost::mutex mx;
|
||||
boost::condition_variable cv;
|
||||
|
||||
BattleState battle;
|
||||
std::map<QueryID, std::string> remainingQueries;
|
||||
std::map<int, QueryID> requestToQueryID; //IDs of answer-requests sent to server => query ids (so we can match answer confirmation from server to the query)
|
||||
std::vector<const CGObjectInstance *> objectsBeingVisited;
|
||||
bool ongoingHeroMovement;
|
||||
bool ongoingChannelProbing; // true if AI currently explore bidirectional teleport channel exits
|
||||
|
||||
bool havingTurn;
|
||||
|
||||
public:
|
||||
AIStatus();
|
||||
~AIStatus();
|
||||
void setBattle(BattleState BS);
|
||||
void setMove(bool ongoing);
|
||||
void setChannelProbing(bool ongoing);
|
||||
bool channelProbing();
|
||||
BattleState getBattle();
|
||||
void addQuery(QueryID ID, std::string description);
|
||||
void removeQuery(QueryID ID);
|
||||
int getQueriesCount();
|
||||
void startedTurn();
|
||||
void madeTurn();
|
||||
void waitTillFree();
|
||||
bool haveTurn();
|
||||
void attemptedAnsweringQuery(QueryID queryID, int answerRequestID);
|
||||
void receivedAnswerConfirmation(int answerRequestID, int result);
|
||||
void heroVisit(const CGObjectInstance * obj, bool started);
|
||||
|
||||
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battle;
|
||||
h & remainingQueries;
|
||||
h & requestToQueryID;
|
||||
h & havingTurn;
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_EXPORT VCAI : public CAdventureAI
|
||||
{
|
||||
public:
|
||||
|
||||
friend class FuzzyHelper;
|
||||
friend class ResourceManager;
|
||||
friend class BuildingManager;
|
||||
|
||||
std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
|
||||
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
|
||||
ObjectInstanceID destinationTeleport;
|
||||
int3 destinationTeleportPos;
|
||||
std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
|
||||
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
|
||||
std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
|
||||
|
||||
//part of mainLoop, but accessible from outisde
|
||||
std::vector<Goals::TSubgoal> basicGoals;
|
||||
Goals::TGoalVec goalsToRemove;
|
||||
Goals::TGoalVec goalsToAdd;
|
||||
std::map<Goals::TSubgoal, Goals::TGoalVec> ultimateGoalsFromBasic; //theoreticlaly same goal can fulfill multiple basic goals
|
||||
|
||||
std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
|
||||
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
|
||||
std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
|
||||
std::set<HeroPtr> heroesUnableToExplore; //these heroes will not be polled for exploration in current state of game
|
||||
|
||||
//sets are faster to search, also do not contain duplicates
|
||||
std::set<const CGObjectInstance *> visitableObjs;
|
||||
std::set<const CGObjectInstance *> alreadyVisited;
|
||||
std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
|
||||
std::map<HeroPtr, std::set<HeroPtr>> visitedHeroes; //visited this turn //FIXME: this is just bug workaround
|
||||
|
||||
AIStatus status;
|
||||
std::string battlename;
|
||||
|
||||
std::shared_ptr<CCallback> myCb;
|
||||
|
||||
std::unique_ptr<boost::thread> makingTurn;
|
||||
ObjectInstanceID selectedObject;
|
||||
|
||||
AIhelper * ah;
|
||||
|
||||
VCAI();
|
||||
virtual ~VCAI();
|
||||
|
||||
//TODO: use only smart pointers?
|
||||
void tryRealize(Goals::Explore & g);
|
||||
void tryRealize(Goals::RecruitHero & g);
|
||||
void tryRealize(Goals::VisitTile & g);
|
||||
void tryRealize(Goals::VisitObj & g);
|
||||
void tryRealize(Goals::VisitHero & g);
|
||||
void tryRealize(Goals::BuildThis & g);
|
||||
void tryRealize(Goals::DigAtTile & g);
|
||||
void tryRealize(Goals::Trade & g);
|
||||
void tryRealize(Goals::BuyArmy & g);
|
||||
void tryRealize(Goals::Invalid & g);
|
||||
void tryRealize(Goals::AbstractGoal & g);
|
||||
|
||||
bool isTileNotReserved(const CGHeroInstance * h, int3 t) const; //the tile is not occupied by allied hero and the object is not reserved
|
||||
|
||||
std::string getBattleAIName() const override;
|
||||
|
||||
void init(std::shared_ptr<CCallback> CB) override;
|
||||
void yourTurn() 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;
|
||||
void saveGame(BinarySerializer & h, const int version) override; //saving
|
||||
void loadGame(BinaryDeserializer & h, const int version) override; //loading
|
||||
void finish() 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;
|
||||
|
||||
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 mainLoop();
|
||||
void performTypicalActions();
|
||||
|
||||
void buildArmyIn(const CGTownInstance * t);
|
||||
void striveToGoal(Goals::TSubgoal ultimateGoal);
|
||||
Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal);
|
||||
void endTurn();
|
||||
void wander(HeroPtr h);
|
||||
void setGoal(HeroPtr h, Goals::TSubgoal goal);
|
||||
void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
|
||||
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
|
||||
|
||||
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
|
||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) 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?
|
||||
void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
|
||||
void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
|
||||
void moveCreaturesToHero(const CGTownInstance * t);
|
||||
void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
|
||||
|
||||
bool moveHeroToTile(int3 dst, HeroPtr h);
|
||||
void buildStructure(const CGTownInstance * t, BuildingID building); //TODO: move to BuildingManager
|
||||
|
||||
void lostHero(HeroPtr h); //should remove all references to hero (assigned tasks and so on)
|
||||
void waitTillFree();
|
||||
|
||||
void addVisitableObj(const CGObjectInstance * obj);
|
||||
void markObjectVisited(const CGObjectInstance * obj);
|
||||
void reserveObject(HeroPtr h, const CGObjectInstance * obj); //TODO: reserve all objects that heroes attempt to visit
|
||||
void unreserveObject(HeroPtr h, const CGObjectInstance * obj);
|
||||
|
||||
void markHeroUnableToExplore(HeroPtr h);
|
||||
void markHeroAbleToExplore(HeroPtr h);
|
||||
bool isAbleToExplore(HeroPtr h);
|
||||
void clearPathsInfo();
|
||||
|
||||
void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it
|
||||
void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
|
||||
void validateVisitableObjs();
|
||||
void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
|
||||
void retrieveVisitableObjs();
|
||||
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
||||
|
||||
const CGObjectInstance * lookForArt(int aid) const;
|
||||
bool isAccessible(const int3 & pos) const;
|
||||
HeroPtr getHeroWithGrail() const;
|
||||
|
||||
const CGObjectInstance * getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate);
|
||||
bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
|
||||
//optimization - use one SM for every hero call
|
||||
|
||||
const CGTownInstance * findTownWithTavern() const;
|
||||
bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
|
||||
|
||||
Goals::TSubgoal getGoal(HeroPtr h) const;
|
||||
bool canAct(HeroPtr h) const;
|
||||
std::vector<HeroPtr> getUnblockedHeroes() const;
|
||||
std::vector<HeroPtr> getMyHeroes() const;
|
||||
HeroPtr primaryHero() const;
|
||||
void checkHeroArmy(HeroPtr h);
|
||||
|
||||
void requestSent(const CPackForServer * pack, int requestID) override;
|
||||
void answerQuery(QueryID queryID, int selection);
|
||||
//special function that can be called ONLY from game events handling thread and will send request ASAP
|
||||
void requestActionASAP(std::function<void()> whatToDo);
|
||||
|
||||
#if 0
|
||||
//disabled due to issue 2890
|
||||
template<typename Handler> void registerGoals(Handler & h)
|
||||
{
|
||||
//h.template registerType<Goals::AbstractGoal, Goals::BoostHero>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::Build>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::BuildThis>();
|
||||
//h.template registerType<Goals::AbstractGoal, Goals::CIssueCommand>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::ClearWayTo>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::CollectRes>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::Conquer>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::DigAtTile>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::Explore>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::FindObj>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::GatherArmy>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::GatherTroops>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::GetArtOfType>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::VisitObj>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::Invalid>();
|
||||
//h.template registerType<Goals::AbstractGoal, Goals::NotLose>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::RecruitHero>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::VisitHero>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::VisitTile>();
|
||||
h.template registerType<Goals::AbstractGoal, Goals::Win>();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename Handler> void serializeInternal(Handler & h, const int version)
|
||||
{
|
||||
h & knownTeleportChannels;
|
||||
h & knownSubterraneanGates;
|
||||
h & destinationTeleport;
|
||||
h & townVisitsThisWeek;
|
||||
|
||||
#if 0
|
||||
//disabled due to issue 2890
|
||||
h & lockedHeroes;
|
||||
#else
|
||||
{
|
||||
ui32 length = 0;
|
||||
h & length;
|
||||
if(!h.saving)
|
||||
{
|
||||
std::set<ui32> loadedPointers;
|
||||
lockedHeroes.clear();
|
||||
for(ui32 index = 0; index < length; index++)
|
||||
{
|
||||
HeroPtr ignored1;
|
||||
h & ignored1;
|
||||
|
||||
ui8 flag = 0;
|
||||
h & flag;
|
||||
|
||||
if(flag)
|
||||
{
|
||||
ui32 pid = 0xffffffff;
|
||||
h & pid;
|
||||
|
||||
if(!vstd::contains(loadedPointers, pid))
|
||||
{
|
||||
loadedPointers.insert(pid);
|
||||
|
||||
ui16 typeId = 0;
|
||||
//this is the problem requires such hack
|
||||
//we have to explicitly ignore invalid goal class type id
|
||||
h & typeId;
|
||||
Goals::AbstractGoal ignored2;
|
||||
ignored2.serialize(h, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
h & reservedHeroesMap; //FIXME: cannot instantiate abstract class
|
||||
h & visitableObjs;
|
||||
h & alreadyVisited;
|
||||
h & reservedObjs;
|
||||
if (version < 788 && !h.saving)
|
||||
{
|
||||
TResources saving;
|
||||
h & saving; //mind the ambiguity
|
||||
}
|
||||
h & status;
|
||||
h & battlename;
|
||||
h & heroesUnableToExplore;
|
||||
|
||||
//myCB is restored after load by init call
|
||||
}
|
||||
};
|
||||
|
||||
class cannotFulfillGoalException : public std::exception
|
||||
{
|
||||
std::string msg;
|
||||
|
||||
public:
|
||||
explicit cannotFulfillGoalException(crstring _Message)
|
||||
: msg(_Message)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~cannotFulfillGoalException() throw ()
|
||||
{
|
||||
};
|
||||
|
||||
const char * what() const throw () override
|
||||
{
|
||||
return msg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class goalFulfilledException : public std::exception
|
||||
{
|
||||
std::string msg;
|
||||
|
||||
public:
|
||||
Goals::TSubgoal goal;
|
||||
|
||||
explicit goalFulfilledException(Goals::TSubgoal Goal)
|
||||
: goal(Goal)
|
||||
{
|
||||
msg = goal->name();
|
||||
}
|
||||
|
||||
virtual ~goalFulfilledException() throw ()
|
||||
{
|
||||
};
|
||||
|
||||
const char * what() const throw () override
|
||||
{
|
||||
return msg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
void makePossibleUpgrades(const CArmedInstance * obj);
|
244
AI/Nullkiller/VCAI.vcxproj
Normal file
244
AI/Nullkiller/VCAI.vcxproj
Normal file
@ -0,0 +1,244 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="RD|Win32">
|
||||
<Configuration>RD</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="RD|x64">
|
||||
<Configuration>RD</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{276C3DB0-7A6B-4417-8E5C-322B08633AAC}</ProjectGuid>
|
||||
<RootNamespace>StupidAI</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\VCMI_global_debug.props" />
|
||||
<Import Project="..\..\VCMI_global.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\VCMI_global_debug.props" />
|
||||
<Import Project="..\..\VCMI_global.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\VCMI_global_release.props" />
|
||||
<Import Project="..\..\VCMI_global.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\VCMI_global_release.props" />
|
||||
<Import Project="..\..\VCMI_global.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>$(VCMI_Out)\AI\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>$(VCMI_Out)\AI\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
|
||||
<OutDir>$(VCMI_Out)/AI</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
|
||||
<OutDir>$(VCMI_Out)\AI\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(FUZZYLITEDIR)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
||||
<AdditionalOptions>/Zm210 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\libs;..\..;..</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
||||
<AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(VCMI_Out);$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\FuzzyLite\fuzzylite</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(VCMI_Out);$(SolutionDir)\AI</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/d2:-notypeopt %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
||||
<AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(VCMI_Out);$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AIhelper.cpp" />
|
||||
<ClCompile Include="AIUtility.cpp" />
|
||||
<ClCompile Include="BuildingManager.cpp" />
|
||||
<ClCompile Include="FuzzyEngines.cpp" />
|
||||
<ClCompile Include="FuzzyHelper.cpp" />
|
||||
<ClCompile Include="Goals\AbstractGoal.cpp" />
|
||||
<ClCompile Include="Goals\AdventureSpellCast.cpp" />
|
||||
<ClCompile Include="Goals\Build.cpp" />
|
||||
<ClCompile Include="Goals\BuildBoat.cpp" />
|
||||
<ClCompile Include="Goals\BuildThis.cpp" />
|
||||
<ClCompile Include="Goals\BuyArmy.cpp" />
|
||||
<ClCompile Include="Goals\ClearWayTo.cpp" />
|
||||
<ClCompile Include="Goals\CollectRes.cpp" />
|
||||
<ClCompile Include="Goals\CompleteQuest.cpp" />
|
||||
<ClCompile Include="Goals\Conquer.cpp" />
|
||||
<ClCompile Include="Goals\DigAtTile.cpp" />
|
||||
<ClCompile Include="Goals\Explore.cpp" />
|
||||
<ClCompile Include="Goals\FindObj.cpp" />
|
||||
<ClCompile Include="Goals\GatherArmy.cpp" />
|
||||
<ClCompile Include="Goals\GatherTroops.cpp" />
|
||||
<ClCompile Include="Goals\GetArtOfType.cpp" />
|
||||
<ClCompile Include="Goals\RecruitHero.cpp" />
|
||||
<ClCompile Include="Goals\Trade.cpp" />
|
||||
<ClCompile Include="Goals\VisitHero.cpp" />
|
||||
<ClCompile Include="Goals\VisitObj.cpp" />
|
||||
<ClCompile Include="Goals\VisitTile.cpp" />
|
||||
<ClCompile Include="Goals\Win.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="MapObjectsEvaluator.cpp" />
|
||||
<ClInclude Include="Pathfinding\Actions\BattleAction.h" />
|
||||
<ClInclude Include="Pathfinding\Actions\TownPortalAction.h" />
|
||||
<ClCompile Include="Pathfinding\Actions\BattleAction.cpp" />
|
||||
<ClCompile Include="Pathfinding\Actions\TownPortalAction.cpp" />
|
||||
<ClInclude Include="Pathfinding\Actions\BoatActions.h" />
|
||||
<ClCompile Include="Pathfinding\Actions\BoatActions.cpp" />
|
||||
<ClCompile Include="Pathfinding\AINodeStorage.cpp" />
|
||||
<ClCompile Include="Pathfinding\AIPathfinder.cpp" />
|
||||
<ClCompile Include="Pathfinding\AIPathfinderConfig.cpp" />
|
||||
<ClCompile Include="Pathfinding\PathfindingManager.cpp" />
|
||||
<ClCompile Include="Pathfinding\Rules\AILayerTransitionRule.cpp" />
|
||||
<ClCompile Include="Pathfinding\Rules\AIMovementAfterDestinationRule.cpp" />
|
||||
<ClCompile Include="Pathfinding\Rules\AIMovementToDestinationRule.cpp" />
|
||||
<ClCompile Include="Pathfinding\Rules\AIPreviousNodeRule.cpp" />
|
||||
<ClCompile Include="ResourceManager.cpp" />
|
||||
<ClCompile Include="SectorMap.cpp" />
|
||||
<ClCompile Include="StdInc.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VCAI.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AIhelper.h" />
|
||||
<ClInclude Include="AIUtility.h" />
|
||||
<ClInclude Include="BuildingManager.h" />
|
||||
<ClInclude Include="FuzzyEngines.h" />
|
||||
<ClInclude Include="FuzzyHelper.h" />
|
||||
<ClInclude Include="Goals\AbstractGoal.h" />
|
||||
<ClInclude Include="Goals\AdventureSpellCast.h" />
|
||||
<ClInclude Include="Goals\Build.h" />
|
||||
<ClInclude Include="Goals\BuildBoat.h" />
|
||||
<ClInclude Include="Goals\BuildThis.h" />
|
||||
<ClInclude Include="Goals\BuyArmy.h" />
|
||||
<ClInclude Include="Goals\CGoal.h" />
|
||||
<ClInclude Include="Goals\ClearWayTo.h" />
|
||||
<ClInclude Include="Goals\CollectRes.h" />
|
||||
<ClInclude Include="Goals\CompleteQuest.h" />
|
||||
<ClInclude Include="Goals\Conquer.h" />
|
||||
<ClInclude Include="Goals\DigAtTile.h" />
|
||||
<ClInclude Include="Goals\Explore.h" />
|
||||
<ClInclude Include="Goals\FindObj.h" />
|
||||
<ClInclude Include="Goals\GatherArmy.h" />
|
||||
<ClInclude Include="Goals\GatherTroops.h" />
|
||||
<ClInclude Include="Goals\GetArtOfType.h" />
|
||||
<ClInclude Include="Goals\Goals.h" />
|
||||
<ClInclude Include="Goals\Invalid.h" />
|
||||
<ClInclude Include="Goals\RecruitHero.h" />
|
||||
<ClInclude Include="Goals\Trade.h" />
|
||||
<ClInclude Include="Goals\VisitHero.h" />
|
||||
<ClInclude Include="Goals\VisitObj.h" />
|
||||
<ClInclude Include="Goals\VisitTile.h" />
|
||||
<ClInclude Include="Goals\Win.h" />
|
||||
<ClInclude Include="MapObjectsEvaluator.h" />
|
||||
<ClInclude Include="Pathfinding\Actions\ISpecialAction.h" />
|
||||
<ClInclude Include="Pathfinding\AINodeStorage.h" />
|
||||
<ClInclude Include="Pathfinding\AIPathfinder.h" />
|
||||
<ClInclude Include="Pathfinding\AIPathfinderConfig.h" />
|
||||
<ClInclude Include="Pathfinding\PathfindingManager.h" />
|
||||
<ClInclude Include="Pathfinding\Rules\AILayerTransitionRule.h" />
|
||||
<ClInclude Include="Pathfinding\Rules\AIMovementAfterDestinationRule.h" />
|
||||
<ClInclude Include="Pathfinding\Rules\AIMovementToDestinationRule.h" />
|
||||
<ClInclude Include="Pathfinding\Rules\AIPreviousNodeRule.h" />
|
||||
<ClInclude Include="ResourceManager.h" />
|
||||
<ClInclude Include="SectorMap.h" />
|
||||
<ClInclude Include="StdInc.h" />
|
||||
<ClInclude Include="VCAI.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
252
AI/Nullkiller/VCAI.vcxproj.filters
Normal file
252
AI/Nullkiller/VCAI.vcxproj.filters
Normal file
@ -0,0 +1,252 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AIhelper.cpp" />
|
||||
<ClCompile Include="AIUtility.cpp" />
|
||||
<ClCompile Include="BuildingManager.cpp" />
|
||||
<ClCompile Include="FuzzyEngines.cpp" />
|
||||
<ClCompile Include="FuzzyHelper.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="MapObjectsEvaluator.cpp" />
|
||||
<ClCompile Include="ResourceManager.cpp" />
|
||||
<ClCompile Include="SectorMap.cpp" />
|
||||
<ClCompile Include="StdInc.cpp" />
|
||||
<ClCompile Include="VCAI.cpp" />
|
||||
<ClCompile Include="Pathfinding\AINodeStorage.cpp">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\AIPathfinder.cpp">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\AIPathfinderConfig.cpp">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\PathfindingManager.cpp">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\AbstractGoal.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\Build.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\BuildBoat.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\BuildThis.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\BuyArmy.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\ClearWayTo.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\CollectRes.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\Conquer.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\DigAtTile.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\Explore.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\FindObj.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\GatherArmy.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\GatherTroops.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\GetArtOfType.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\RecruitHero.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\Trade.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\VisitHero.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\VisitObj.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\VisitTile.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\Win.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\CompleteQuest.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Goals\AdventureSpellCast.cpp">
|
||||
<Filter>Goals</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\Actions\BoatActions.cpp">
|
||||
<Filter>Pathfinding\Actions</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\Actions\BattleAction.cpp">
|
||||
<Filter>Pathfinding\Actions</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\Actions\TownPortalAction.cpp">
|
||||
<Filter>Pathfinding\Actions</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\Rules\AILayerTransitionRule.cpp">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\Rules\AIMovementAfterDestinationRule.cpp">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\Rules\AIMovementToDestinationRule.cpp">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pathfinding\Rules\AIPreviousNodeRule.cpp">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AIhelper.h" />
|
||||
<ClInclude Include="AIUtility.h" />
|
||||
<ClInclude Include="BuildingManager.h" />
|
||||
<ClInclude Include="FuzzyEngines.h" />
|
||||
<ClInclude Include="FuzzyHelper.h" />
|
||||
<ClInclude Include="MapObjectsEvaluator.h" />
|
||||
<ClInclude Include="ResourceManager.h" />
|
||||
<ClInclude Include="SectorMap.h" />
|
||||
<ClInclude Include="StdInc.h" />
|
||||
<ClInclude Include="VCAI.h" />
|
||||
<ClInclude Include="Pathfinding\AINodeStorage.h">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\AIPathfinder.h">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\AIPathfinderConfig.h">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\PathfindingManager.h">
|
||||
<Filter>Pathfinding</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\AbstractGoal.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\Build.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\BuildBoat.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\BuildThis.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\BuyArmy.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\CGoal.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\ClearWayTo.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\CollectRes.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\Conquer.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\DigAtTile.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\Explore.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\FindObj.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\GatherArmy.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\GatherTroops.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\GetArtOfType.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\Goals.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\Invalid.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\RecruitHero.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\Trade.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\VisitHero.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\VisitObj.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\VisitTile.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\Win.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\CompleteQuest.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Goals\AdventureSpellCast.h">
|
||||
<Filter>Goals</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Actions\ISpecialAction.h">
|
||||
<Filter>Pathfinding\Actions</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Actions\BoatActions.h">
|
||||
<Filter>Pathfinding\Actions</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Actions\BattleAction.h">
|
||||
<Filter>Pathfinding\Actions</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Actions\TownPortalAction.h">
|
||||
<Filter>Pathfinding\Actions</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Rules\AILayerTransitionRule.h">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Rules\AIMovementAfterDestinationRule.h">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Rules\AIMovementToDestinationRule.h">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pathfinding\Rules\AIPreviousNodeRule.h">
|
||||
<Filter>Pathfinding\Rules</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Pathfinding">
|
||||
<UniqueIdentifier>{f0ef4866-37a3-4a10-a6bf-34460fcefab5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Goals">
|
||||
<UniqueIdentifier>{f97140a0-eee3-456f-b586-4b13265c01da}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Pathfinding\Rules">
|
||||
<UniqueIdentifier>{beabfdb9-2e76-4daa-8d1a-81086387f319}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Pathfinding\Actions">
|
||||
<UniqueIdentifier>{3ebb4852-a986-447a-b5cc-20992df76f0c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
32
AI/Nullkiller/main.cpp
Normal file
32
AI/Nullkiller/main.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* main.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 "VCAI.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define strcpy_s(a, b, c) strncpy(a, c, b)
|
||||
#endif
|
||||
|
||||
static const char * g_cszAiName = "VCAI";
|
||||
|
||||
extern "C" DLL_EXPORT int GetGlobalAiVersion()
|
||||
{
|
||||
return AI_INTERFACE_VER;
|
||||
}
|
||||
|
||||
extern "C" DLL_EXPORT void GetAiName(char * name)
|
||||
{
|
||||
strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
|
||||
}
|
||||
|
||||
extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> & out)
|
||||
{
|
||||
out = std::make_shared<VCAI>();
|
||||
}
|
Loading…
Reference in New Issue
Block a user