1
0
mirror of https://github.com/vcmi/vcmi.git synced 2026-05-22 09:55:17 +02:00

NKAI: allow multiple tasks to be executed from one calculation

This commit is contained in:
Andrii Danylchenko
2024-04-14 15:23:44 +03:00
parent 9390825ee7
commit ed76d8a652
33 changed files with 559 additions and 219 deletions
+5 -3
View File
@@ -31,12 +31,12 @@ TTask Goals::taskptr(const AbstractGoal & tmp)
if(!tmp.isElementar())
throw cannotFulfillGoalException(tmp.toString() + " is not elementar");
ptr.reset(dynamic_cast<ITask *>(tmp.clone()));
ptr.reset(tmp.clone()->asTask());
return ptr;
}
std::string AbstractGoal::toString() const //TODO: virtualize
std::string AbstractGoal::toString() const
{
std::string desc;
switch(goalType)
@@ -63,8 +63,10 @@ std::string AbstractGoal::toString() const //TODO: virtualize
default:
return std::to_string(goalType);
}
if(hero.get(true)) //FIXME: used to crash when we lost hero and failed goal
if(hero)
desc += " (" + hero->getNameTranslated() + ")";
return desc;
}
+12 -5
View File
@@ -10,9 +10,8 @@
#pragma once
#include "../../../lib/VCMI_Lib.h"
#include "../../../lib/CBuildingHandler.h"
#include "../../../lib/CCreatureHandler.h"
#include "../../../lib/CTownHandler.h"
#include "../../../lib/mapObjects/CGTownInstance.h"
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../AIUtility.h"
namespace NKAI
@@ -106,7 +105,7 @@ namespace Goals
int objid; SETTER(int, objid)
int aid; SETTER(int, aid)
int3 tile; SETTER(int3, tile)
HeroPtr hero; SETTER(HeroPtr, hero)
const CGHeroInstance * hero; SETTER(CGHeroInstance *, hero)
const CGTownInstance *town; SETTER(CGTownInstance *, town)
int bid; SETTER(int, bid)
@@ -119,6 +118,7 @@ namespace Goals
objid = -1;
tile = int3(-1, -1, -1);
town = nullptr;
hero = nullptr;
bid = -1;
goldCost = 0;
}
@@ -147,6 +147,11 @@ namespace Goals
virtual bool hasHash() const { return false; }
virtual uint64_t getHash() const { return 0; }
virtual ITask * asTask()
{
throw std::runtime_error("Abstract goal is not a task");
}
bool operator!=(const AbstractGoal & g) const
{
@@ -165,9 +170,11 @@ namespace Goals
//TODO: make accept work for std::shared_ptr... somehow
virtual void accept(AIGateway * ai) = 0; //unhandled goal will report standard error
virtual std::string toString() const = 0;
virtual HeroPtr getHero() const = 0;
virtual const CGHeroInstance * getHero() const = 0;
virtual ~ITask() {}
virtual int getHeroExchangeCount() const = 0;
virtual bool isObjectAffected(ObjectInstanceID h) const = 0;
virtual std::vector<ObjectInstanceID> getAffectedObjects() const = 0;
};
}
+3 -3
View File
@@ -18,12 +18,12 @@ using namespace Goals;
bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const
{
return hero.h == other.hero.h;
return hero == other.hero;
}
void AdventureSpellCast::accept(AIGateway * ai)
{
if(!hero.validAndSet())
if(!hero)
throw cannotFulfillGoalException("Invalid hero!");
auto spell = getSpell();
@@ -56,7 +56,7 @@ void AdventureSpellCast::accept(AIGateway * ai)
auto wait = cb->waitTillRealize;
cb->waitTillRealize = true;
cb->castSpell(hero.h, spellID, tile);
cb->castSpell(hero, spellID, tile);
if(town && spellID == SpellID::TOWN_PORTAL)
{
+1 -1
View File
@@ -22,7 +22,7 @@ namespace Goals
SpellID spellID;
public:
AdventureSpellCast(HeroPtr hero, SpellID spellID)
AdventureSpellCast(const CGHeroInstance * hero, SpellID spellID)
: ElementarGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID)
{
sethero(hero);
+29 -2
View File
@@ -14,7 +14,6 @@
namespace NKAI
{
struct HeroPtr;
class AIGateway;
namespace Goals
@@ -92,9 +91,37 @@ namespace Goals
bool isElementar() const override { return true; }
HeroPtr getHero() const override { return AbstractGoal::hero; }
const CGHeroInstance * getHero() const override { return AbstractGoal::hero; }
int getHeroExchangeCount() const override { return 0; }
bool isObjectAffected(ObjectInstanceID id) const override
{
return (AbstractGoal::hero && AbstractGoal::hero->id == id)
|| AbstractGoal::objid == id
|| (AbstractGoal::town && AbstractGoal::town->id == id);
}
std::vector<ObjectInstanceID> getAffectedObjects() const override
{
auto result = std::vector<ObjectInstanceID>();
if(AbstractGoal::hero)
result.push_back(AbstractGoal::hero->id);
if(AbstractGoal::objid != -1)
result.push_back(ObjectInstanceID(AbstractGoal::objid));
if(AbstractGoal::town)
result.push_back(AbstractGoal::town->id);
return result;
}
ITask * asTask() override
{
return this;
}
};
}
+32
View File
@@ -117,4 +117,36 @@ int Composition::getHeroExchangeCount() const
return result;
}
std::vector<ObjectInstanceID> Composition::getAffectedObjects() const
{
std::vector<ObjectInstanceID> affectedObjects;
for(auto sequence : subtasks)
{
for(auto task : sequence)
{
if(task->isElementar())
vstd::concatenate(affectedObjects, task->asTask()->getAffectedObjects());
}
}
vstd::removeDuplicates(affectedObjects);
return affectedObjects;
}
bool Composition::isObjectAffected(ObjectInstanceID id) const
{
for(auto sequence : subtasks)
{
for(auto task : sequence)
{
if(task->isElementar() && task->asTask()->isObjectAffected(id))
return true;
}
}
return false;
}
}
+3
View File
@@ -35,6 +35,9 @@ namespace Goals
TGoalVec decompose(const Nullkiller * ai) const override;
bool isElementar() const override;
int getHeroExchangeCount() const override;
std::vector<ObjectInstanceID> getAffectedObjects() const override;
bool isObjectAffected(ObjectInstanceID id) const override;
};
}
+1 -1
View File
@@ -20,7 +20,7 @@ using namespace Goals;
bool DigAtTile::operator==(const DigAtTile & other) const
{
return other.hero.h == hero.h && other.tile == tile;
return other.hero == hero && other.tile == tile;
}
//
//TSubgoal DigAtTile::decomposeSingle() const
+4 -4
View File
@@ -18,22 +18,22 @@ using namespace Goals;
bool DismissHero::operator==(const DismissHero & other) const
{
return hero.h == other.hero.h;
return hero == other.hero;
}
void DismissHero::accept(AIGateway * ai)
{
if(!hero.validAndSet())
if(!hero)
throw cannotFulfillGoalException("Invalid hero!");
cb->dismissHero(hero.h);
cb->dismissHero(hero);
throw goalFulfilledException(sptr(*this));
}
std::string DismissHero::toString() const
{
return "DismissHero " + hero.name;
return "DismissHero " + heroName;
}
}
+5 -1
View File
@@ -17,11 +17,15 @@ namespace Goals
{
class DLL_EXPORT DismissHero : public ElementarGoal<DismissHero>
{
private:
std::string heroName;
public:
DismissHero(HeroPtr hero)
DismissHero(const CGHeroInstance * hero)
: ElementarGoal(Goals::DISMISS_HERO)
{
sethero(hero);
heroName = hero->getNameTranslated();
}
void accept(AIGateway * ai) override;
@@ -26,6 +26,26 @@ ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(
{
}
std::vector<ObjectInstanceID> ExchangeSwapTownHeroes::getAffectedObjects() const
{
std::vector<ObjectInstanceID> affectedObjects = { town->id };
if(town->garrisonHero)
affectedObjects.push_back(town->garrisonHero->id);
if(town->visitingHero)
affectedObjects.push_back(town->visitingHero->id);
return affectedObjects;
}
bool ExchangeSwapTownHeroes::isObjectAffected(ObjectInstanceID id) const
{
return town->id == id
|| (town->visitingHero && town->visitingHero->id == id)
|| (town->garrisonHero && town->garrisonHero->id == id);
}
std::string ExchangeSwapTownHeroes::toString() const
{
return "Exchange and swap heroes of " + town->getNameTranslated();
@@ -35,6 +35,9 @@ namespace Goals
const CGHeroInstance * getGarrisonHero() const { return garrisonHero; }
HeroLockedReason getLockingReason() const { return lockingReason; }
std::vector<ObjectInstanceID> getAffectedObjects() const override;
bool isObjectAffected(ObjectInstanceID id) const override;
};
}
+32
View File
@@ -42,6 +42,38 @@ bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const
&& chainPath.chainMask == other.chainPath.chainMask;
}
std::vector<ObjectInstanceID> ExecuteHeroChain::getAffectedObjects() const
{
std::vector<ObjectInstanceID> affectedObjects = { chainPath.targetHero->id };
if(objid != -1)
affectedObjects.push_back(ObjectInstanceID(objid));
for(auto & node : chainPath.nodes)
{
if(node.targetHero)
affectedObjects.push_back(node.targetHero->id);
}
vstd::removeDuplicates(affectedObjects);
return affectedObjects;
}
bool ExecuteHeroChain::isObjectAffected(ObjectInstanceID id) const
{
if(chainPath.targetHero->id == id || objid == id)
return true;
for(auto & node : chainPath.nodes)
{
if(node.targetHero && node.targetHero->id == id)
return true;
}
return false;
}
void ExecuteHeroChain::accept(AIGateway * ai)
{
logAi->debug("Executing hero chain towards %s. Path %s", targetName, chainPath.toString());
+3
View File
@@ -34,6 +34,9 @@ namespace Goals
int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
std::vector<ObjectInstanceID> getAffectedObjects() const override;
bool isObjectAffected(ObjectInstanceID id) const override;
private:
bool moveHeroToTile(AIGateway * ai, const CGHeroInstance * hero, const int3 & tile);
};
+1 -1
View File
@@ -46,7 +46,7 @@ void StayAtTown::accept(AIGateway * ai)
logAi->error("Hero %s expected visiting town %s", hero->getNameTranslated(), town->getNameTranslated());
}
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::DEFENCE);
ai->nullkiller->lockHero(hero, HeroLockedReason::DEFENCE);
}
}