mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-26 03:52:01 +02:00
AI: CompleteQuest goal and summon boat spell support
This commit is contained in:
parent
e996879733
commit
5d022ba77c
@ -29,6 +29,7 @@ set(VCAI_SRCS
|
|||||||
Goals/GatherArmy.cpp
|
Goals/GatherArmy.cpp
|
||||||
Goals/GatherTroops.cpp
|
Goals/GatherTroops.cpp
|
||||||
Goals/BuyArmy.cpp
|
Goals/BuyArmy.cpp
|
||||||
|
Goals/AdventureSpellCast.cpp
|
||||||
Goals/Win.cpp
|
Goals/Win.cpp
|
||||||
Goals/VisitTile.cpp
|
Goals/VisitTile.cpp
|
||||||
Goals/VisitObj.cpp
|
Goals/VisitObj.cpp
|
||||||
@ -41,6 +42,7 @@ set(VCAI_SRCS
|
|||||||
Goals/DigAtTile.cpp
|
Goals/DigAtTile.cpp
|
||||||
Goals/GetArtOfType.cpp
|
Goals/GetArtOfType.cpp
|
||||||
Goals/FindObj.cpp
|
Goals/FindObj.cpp
|
||||||
|
Goals/CompleteQuest.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
VCAI.cpp
|
VCAI.cpp
|
||||||
)
|
)
|
||||||
@ -71,6 +73,7 @@ set(VCAI_HEADERS
|
|||||||
Goals/GatherArmy.h
|
Goals/GatherArmy.h
|
||||||
Goals/GatherTroops.h
|
Goals/GatherTroops.h
|
||||||
Goals/BuyArmy.h
|
Goals/BuyArmy.h
|
||||||
|
Goals/AdventureSpellCast.h
|
||||||
Goals/Win.h
|
Goals/Win.h
|
||||||
Goals/VisitTile.h
|
Goals/VisitTile.h
|
||||||
Goals/VisitObj.h
|
Goals/VisitObj.h
|
||||||
@ -83,6 +86,7 @@ set(VCAI_HEADERS
|
|||||||
Goals/DigAtTile.h
|
Goals/DigAtTile.h
|
||||||
Goals/GetArtOfType.h
|
Goals/GetArtOfType.h
|
||||||
Goals/FindObj.h
|
Goals/FindObj.h
|
||||||
|
Goals/CompleteQuest.h
|
||||||
Goals/Goals.h
|
Goals/Goals.h
|
||||||
VCAI.h
|
VCAI.h
|
||||||
)
|
)
|
||||||
|
@ -93,6 +93,19 @@ float FuzzyHelper::evaluate(Goals::BuildBoat & g)
|
|||||||
return g.parent->accept(this) - buildBoatPenalty;
|
return g.parent->accept(this) - buildBoatPenalty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
float FuzzyHelper::evaluate(Goals::VisitObj & g)
|
||||||
{
|
{
|
||||||
return visitObjEngine.evaluate(g);
|
return visitObjEngine.evaluate(g);
|
||||||
|
@ -32,6 +32,7 @@ public:
|
|||||||
float evaluate(Goals::BuildBoat & g);
|
float evaluate(Goals::BuildBoat & g);
|
||||||
float evaluate(Goals::GatherArmy & g);
|
float evaluate(Goals::GatherArmy & g);
|
||||||
float evaluate(Goals::ClearWayTo & g);
|
float evaluate(Goals::ClearWayTo & g);
|
||||||
|
float evaluate(Goals::CompleteQuest & g);
|
||||||
float evaluate(Goals::Invalid & g);
|
float evaluate(Goals::Invalid & g);
|
||||||
float evaluate(Goals::AbstractGoal & g);
|
float evaluate(Goals::AbstractGoal & g);
|
||||||
void setPriority(Goals::TSubgoal & g);
|
void setPriority(Goals::TSubgoal & g);
|
||||||
|
@ -40,8 +40,6 @@ std::string AbstractGoal::name() const //TODO: virtualize
|
|||||||
return "INVALID";
|
return "INVALID";
|
||||||
case WIN:
|
case WIN:
|
||||||
return "WIN";
|
return "WIN";
|
||||||
case DO_NOT_LOSE:
|
|
||||||
return "DO NOT LOOSE";
|
|
||||||
case CONQUER:
|
case CONQUER:
|
||||||
return "CONQUER";
|
return "CONQUER";
|
||||||
case BUILD:
|
case BUILD:
|
||||||
@ -95,8 +93,6 @@ std::string AbstractGoal::name() const //TODO: virtualize
|
|||||||
case GET_ART_TYPE:
|
case GET_ART_TYPE:
|
||||||
desc = "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name();
|
desc = "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name();
|
||||||
break;
|
break;
|
||||||
case ISSUE_COMMAND:
|
|
||||||
return "ISSUE COMMAND (unsupported)";
|
|
||||||
case VISIT_TILE:
|
case VISIT_TILE:
|
||||||
desc = "VISIT TILE " + tile.toString();
|
desc = "VISIT TILE " + tile.toString();
|
||||||
break;
|
break;
|
||||||
|
@ -37,6 +37,35 @@ namespace Goals
|
|||||||
class ClearWayTo;
|
class ClearWayTo;
|
||||||
class Invalid;
|
class Invalid;
|
||||||
class Trade;
|
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>
|
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||||
{
|
{
|
||||||
@ -48,35 +77,6 @@ namespace Goals
|
|||||||
|
|
||||||
typedef std::vector<TSubgoal> TGoalVec;
|
typedef std::vector<TSubgoal> TGoalVec;
|
||||||
|
|
||||||
enum EGoals
|
|
||||||
{
|
|
||||||
INVALID = -1,
|
|
||||||
WIN, DO_NOT_LOSE, CONQUER, BUILD, //build needs to get a real reasoning
|
|
||||||
EXPLORE, GATHER_ARMY,
|
|
||||||
BOOST_HERO,
|
|
||||||
RECRUIT_HERO,
|
|
||||||
BUILD_STRUCTURE, //if hero set, then in visited town
|
|
||||||
COLLECT_RES,
|
|
||||||
GATHER_TROOPS, // val of creatures with objid
|
|
||||||
|
|
||||||
OBJECT_GOALS_BEGIN,
|
|
||||||
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,
|
|
||||||
|
|
||||||
//BUILD_STRUCTURE,
|
|
||||||
ISSUE_COMMAND,
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
//method chaining + clone pattern
|
//method chaining + clone pattern
|
||||||
#define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;};
|
#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; };
|
#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) override { field = rhs; return *this; };
|
||||||
|
63
AI/VCAI/Goals/AdventureSpellCast.cpp
Normal file
63
AI/VCAI/Goals/AdventureSpellCast.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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 = spellID.toSpell();
|
||||||
|
|
||||||
|
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(!vstd::contains(hero->spells, spellID))
|
||||||
|
throw cannotFulfillGoalException("Hero has no " + spell->name);
|
||||||
|
|
||||||
|
if(hero->mana < hero->getSpellCost(spellID.toSpell()))
|
||||||
|
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->name);
|
||||||
|
|
||||||
|
return iAmElementar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdventureSpellCast::accept(VCAI * ai)
|
||||||
|
{
|
||||||
|
cb->castSpell(hero.h, spellID, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AdventureSpellCast::name() const
|
||||||
|
{
|
||||||
|
return "AdventureSpellCast " + spellID.toSpell()->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AdventureSpellCast::completeMessage() const
|
||||||
|
{
|
||||||
|
return "Spell casted successfully " + spellID.toSpell()->name;
|
||||||
|
}
|
39
AI/VCAI/Goals/AdventureSpellCast.h
Normal file
39
AI/VCAI/Goals/AdventureSpellCast.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
@ -60,11 +60,13 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
||||||
}
|
}
|
||||||
TSubgoal iAmElementar()
|
TSubgoal iAmElementar() const
|
||||||
{
|
{
|
||||||
setisElementar(true); //FIXME: it's not const-correct, maybe we shoudl only set returned clone?
|
|
||||||
TSubgoal ptr;
|
TSubgoal ptr;
|
||||||
|
|
||||||
ptr.reset(clone());
|
ptr.reset(clone());
|
||||||
|
ptr->setisElementar(true);
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
template<typename Handler> void serialize(Handler & h, const int version)
|
template<typename Handler> void serialize(Handler & h, const int version)
|
||||||
@ -79,7 +81,7 @@ namespace Goals
|
|||||||
if(goalType != g.goalType)
|
if(goalType != g.goalType)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (*this) == (dynamic_cast<const T &>(g));
|
return (*this) == (static_cast<const T &>(g));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool operator==(const T & other) const = 0;
|
virtual bool operator==(const T & other) const = 0;
|
||||||
|
276
AI/VCAI/Goals/CompleteQuest.cpp
Normal file
276
AI/VCAI/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/VCAI/Goals/CompleteQuest.h
Normal file
46
AI/VCAI/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;
|
||||||
|
};
|
||||||
|
}
|
@ -33,6 +33,8 @@ bool Conquer::operator==(const Conquer & other) const
|
|||||||
|
|
||||||
TSubgoal Conquer::whatToDoToAchieve()
|
TSubgoal Conquer::whatToDoToAchieve()
|
||||||
{
|
{
|
||||||
|
logAi->trace("Entering goal CONQUER");
|
||||||
|
|
||||||
return fh->chooseSolution(getAllPossibleSubgoals());
|
return fh->chooseSolution(getAllPossibleSubgoals());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@ int GatherTroops::getCreaturesCount(const CArmedInstance * army)
|
|||||||
|
|
||||||
TSubgoal GatherTroops::whatToDoToAchieve()
|
TSubgoal GatherTroops::whatToDoToAchieve()
|
||||||
{
|
{
|
||||||
|
logAi->trace("Entering GatherTroops::whatToDoToAchieve");
|
||||||
|
|
||||||
auto heroes = cb->getHeroesInfo(true);
|
auto heroes = cb->getHeroesInfo(true);
|
||||||
|
|
||||||
for(auto hero : heroes)
|
for(auto hero : heroes)
|
||||||
|
@ -30,3 +30,5 @@
|
|||||||
#include "ClearWayTo.h"
|
#include "ClearWayTo.h"
|
||||||
#include "DigAtTile.h"
|
#include "DigAtTile.h"
|
||||||
#include "FindObj.h"
|
#include "FindObj.h"
|
||||||
|
#include "CompleteQuest.h"
|
||||||
|
#include "AdventureSpellCast.h"
|
@ -82,6 +82,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat
|
|||||||
|
|
||||||
heroNode.chainMask = 0;
|
heroNode.chainMask = 0;
|
||||||
heroNode.danger = 0;
|
heroNode.danger = 0;
|
||||||
|
heroNode.manaCost = 0;
|
||||||
heroNode.specialAction.reset();
|
heroNode.specialAction.reset();
|
||||||
heroNode.update(coord, layer, accessibility);
|
heroNode.update(coord, layer, accessibility);
|
||||||
}
|
}
|
||||||
@ -97,6 +98,12 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
|
|||||||
dstNode->danger = srcNode->danger;
|
dstNode->danger = srcNode->danger;
|
||||||
dstNode->action = destination.action;
|
dstNode->action = destination.action;
|
||||||
dstNode->theNodeBefore = srcNode->theNodeBefore;
|
dstNode->theNodeBefore = srcNode->theNodeBefore;
|
||||||
|
dstNode->manaCost = srcNode->manaCost;
|
||||||
|
|
||||||
|
if(dstNode->specialAction)
|
||||||
|
{
|
||||||
|
dstNode->specialAction->applyOnDestination(getHero(), destination, source, dstNode, srcNode);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +193,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const
|
|||||||
{
|
{
|
||||||
std::vector<AIPath> paths;
|
std::vector<AIPath> paths;
|
||||||
auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL];
|
auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL];
|
||||||
|
auto initialPos = hero->visitablePos();
|
||||||
|
|
||||||
for(const AIPathNode & node : chains)
|
for(const AIPathNode & node : chains)
|
||||||
{
|
{
|
||||||
@ -197,7 +205,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const
|
|||||||
AIPath path;
|
AIPath path;
|
||||||
const AIPathNode * current = &node;
|
const AIPathNode * current = &node;
|
||||||
|
|
||||||
while(current != nullptr)
|
while(current != nullptr && current->coord != initialPos)
|
||||||
{
|
{
|
||||||
AIPathNodeInfo pathNode;
|
AIPathNodeInfo pathNode;
|
||||||
|
|
||||||
|
@ -15,16 +15,28 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../Goals/AbstractGoal.h"
|
#include "../Goals/AbstractGoal.h"
|
||||||
|
|
||||||
|
class AIPathNode;
|
||||||
|
|
||||||
class ISpecialAction
|
class ISpecialAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const = 0;
|
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const = 0;
|
||||||
|
|
||||||
|
virtual void applyOnDestination(
|
||||||
|
HeroPtr hero,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
const PathNodeInfo & source,
|
||||||
|
AIPathNode * dstMode,
|
||||||
|
const AIPathNode * srcNode) const
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIPathNode : public CGPathNode
|
struct AIPathNode : public CGPathNode
|
||||||
{
|
{
|
||||||
uint32_t chainMask;
|
uint32_t chainMask;
|
||||||
uint64_t danger;
|
uint64_t danger;
|
||||||
|
uint32_t manaCost;
|
||||||
std::shared_ptr<const ISpecialAction> specialAction;
|
std::shared_ptr<const ISpecialAction> specialAction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,10 +48,10 @@ std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile)
|
|||||||
}
|
}
|
||||||
|
|
||||||
storageMap[hero] = nodeStorage;
|
storageMap[hero] = nodeStorage;
|
||||||
|
nodeStorage->setHero(hero.get());
|
||||||
|
|
||||||
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
|
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
|
||||||
|
|
||||||
nodeStorage->setHero(hero.get());
|
|
||||||
cb->calculatePaths(config, hero.get());
|
cb->calculatePaths(config, hero.get());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -16,14 +16,31 @@
|
|||||||
|
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
class BuildBoatAction : public ISpecialAction
|
class VirtualBoatAction : public ISpecialAction
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint64_t specialChain;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VirtualBoatAction(uint64_t specialChain)
|
||||||
|
:specialChain(specialChain)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t getSpecialChain() const
|
||||||
|
{
|
||||||
|
return specialChain;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BuildBoatAction : public VirtualBoatAction
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const IShipyard * shipyard;
|
const IShipyard * shipyard;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BuildBoatAction(const IShipyard * shipyard)
|
BuildBoatAction(const IShipyard * shipyard)
|
||||||
:shipyard(shipyard)
|
:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +50,51 @@ namespace AIPathfinding
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SummonBoatAction : public VirtualBoatAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SummonBoatAction()
|
||||||
|
:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
|
||||||
|
{
|
||||||
|
return sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void applyOnDestination(
|
||||||
|
HeroPtr hero,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
const PathNodeInfo & source,
|
||||||
|
AIPathNode * dstMode,
|
||||||
|
const AIPathNode * srcNode) const override
|
||||||
|
{
|
||||||
|
dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
|
||||||
|
dstMode->theNodeBefore = source.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAffordableBy(HeroPtr hero, const AIPathNode * source) const
|
||||||
|
{
|
||||||
|
logAi->trace(
|
||||||
|
"Hero %s has %d mana and needed %d and already spent %d",
|
||||||
|
hero->name,
|
||||||
|
hero->mana,
|
||||||
|
getManaCost(hero),
|
||||||
|
source->manaCost);
|
||||||
|
|
||||||
|
return hero->mana >= source->manaCost + getManaCost(hero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t getManaCost(HeroPtr hero) const
|
||||||
|
{
|
||||||
|
SpellID summonBoat = SpellID::SUMMON_BOAT;
|
||||||
|
|
||||||
|
return hero->getSpellCost(summonBoat.toSpell());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class BattleAction : public ISpecialAction
|
class BattleAction : public ISpecialAction
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -58,6 +120,7 @@ namespace AIPathfinding
|
|||||||
VCAI * ai;
|
VCAI * ai;
|
||||||
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
|
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
|
||||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||||
|
std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
|
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
|
||||||
@ -79,37 +142,14 @@ namespace AIPathfinding
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL
|
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
|
||||||
&& vstd::contains(virtualBoats, destination.coord))
|
|
||||||
{
|
{
|
||||||
logAi->trace("Bypassing virtual boat at %s!", destination.coord.toString());
|
std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
|
||||||
|
|
||||||
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
|
if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
|
||||||
{
|
{
|
||||||
std::shared_ptr<const BuildBoatAction> virtualBoat = virtualBoats.at(destination.coord);
|
logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
|
||||||
|
}
|
||||||
auto boatNodeOptional = nodeStorage->getOrCreateNode(
|
|
||||||
node->coord,
|
|
||||||
node->layer,
|
|
||||||
node->chainMask | AINodeStorage::RESOURCE_CHAIN);
|
|
||||||
|
|
||||||
if(boatNodeOptional)
|
|
||||||
{
|
|
||||||
AIPathNode * boatNode = boatNodeOptional.get();
|
|
||||||
|
|
||||||
boatNode->specialAction = virtualBoat;
|
|
||||||
destination.blocked = false;
|
|
||||||
destination.action = CGPathNode::ENodeAction::EMBARK;
|
|
||||||
destination.node = boatNode;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logAi->trace(
|
|
||||||
"Can not allocate boat node while moving %s -> %s",
|
|
||||||
source.coord.toString(),
|
|
||||||
destination.coord.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +182,84 @@ namespace AIPathfinding
|
|||||||
logAi->debug("Virtual boat added at %s", boatLocation.toString());
|
logAi->debug("Virtual boat added at %s", boatLocation.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto hero = nodeStorage->getHero();
|
||||||
|
|
||||||
|
if(vstd::contains(hero->spells, SpellID::SUMMON_BOAT))
|
||||||
|
{
|
||||||
|
auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
|
||||||
|
|
||||||
|
if(hero->getSpellSchoolLevel(summonBoatSpell) == SecSkillLevel::EXPERT)
|
||||||
|
{
|
||||||
|
summonableVirtualBoat.reset(new SummonBoatAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const VirtualBoatAction> 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 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::NOT_SET)
|
||||||
|
{
|
||||||
|
boatNode->specialAction = virtualBoat;
|
||||||
|
destination.blocked = false;
|
||||||
|
destination.action = CGPathNode::ENodeAction::EMBARK;
|
||||||
|
destination.node = boatNode;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logAi->trace(
|
||||||
|
"Special transition node already allocated. Blocked moving %s -> %s",
|
||||||
|
source.coord.toString(),
|
||||||
|
destination.coord.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logAi->trace(
|
||||||
|
"Can not allocate special transition node while moving %s -> %s",
|
||||||
|
source.coord.toString(),
|
||||||
|
destination.coord.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "PathfindingManager.h"
|
#include "PathfindingManager.h"
|
||||||
#include "AIPathfinder.h"
|
#include "AIPathfinder.h"
|
||||||
#include "AIPathfinderConfig.h"
|
#include "AIPathfinderConfig.h"
|
||||||
#include "Goals/Goals.h"
|
#include "../Goals/Goals.h"
|
||||||
#include "../../../lib/CGameInfoCallback.h"
|
#include "../../../lib/CGameInfoCallback.h"
|
||||||
#include "../../../lib/mapping/CMap.h"
|
#include "../../../lib/mapping/CMap.h"
|
||||||
|
|
||||||
@ -130,8 +130,6 @@ Goals::TGoalVec PathfindingManager::findPath(
|
|||||||
|
|
||||||
if(isSafeToVisit(hero, danger))
|
if(isSafeToVisit(hero, danger))
|
||||||
{
|
{
|
||||||
logAi->trace("It's safe for %s to visit tile %s with danger %s", hero->name, dest.toString(), std::to_string(danger));
|
|
||||||
|
|
||||||
Goals::TSubgoal solution;
|
Goals::TSubgoal solution;
|
||||||
|
|
||||||
if(path.specialAction)
|
if(path.specialAction)
|
||||||
@ -153,6 +151,8 @@ Goals::TGoalVec PathfindingManager::findPath(
|
|||||||
|
|
||||||
solution->evaluationContext.movementCost += path.movementCost();
|
solution->evaluationContext.movementCost += path.movementCost();
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
result.push_back(solution);
|
result.push_back(solution);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -212,11 +212,15 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
|
|||||||
return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero));
|
return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: we should be able to return apriopriate quest here
|
auto questObj = dynamic_cast<const IQuestObject*>(topObj);
|
||||||
//ret.push_back(ai->questToGoal());
|
|
||||||
//however, visiting obj for firts time will give us quest
|
if(questObj)
|
||||||
//do not access quets guard if we can't complete the quest
|
{
|
||||||
logAi->trace("Can not visit this quest guard! Not ready!");
|
auto questInfo = QuestInfo(questObj->quest, topObj, topObj->visitablePos());
|
||||||
|
|
||||||
|
return sptr(Goals::CompleteQuest(questInfo));
|
||||||
|
}
|
||||||
|
|
||||||
return sptr(Goals::Invalid());
|
return sptr(Goals::Invalid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
168
AI/VCAI/VCAI.cpp
168
AI/VCAI/VCAI.cpp
@ -873,7 +873,7 @@ void VCAI::mainLoop()
|
|||||||
basicGoals.push_back(ah->whatToDo());
|
basicGoals.push_back(ah->whatToDo());
|
||||||
for (auto quest : myCb->getMyQuests())
|
for (auto quest : myCb->getMyQuests())
|
||||||
{
|
{
|
||||||
basicGoals.push_back(questToGoal(quest));
|
basicGoals.push_back(sptr(Goals::CompleteQuest(quest)));
|
||||||
}
|
}
|
||||||
basicGoals.push_back(sptr(Goals::Build()));
|
basicGoals.push_back(sptr(Goals::Build()));
|
||||||
|
|
||||||
@ -1013,11 +1013,13 @@ void VCAI::mainLoop()
|
|||||||
//remove goals we couldn't decompose
|
//remove goals we couldn't decompose
|
||||||
for (auto goal : goalsToRemove)
|
for (auto goal : goalsToRemove)
|
||||||
vstd::erase_if_present(basicGoals, goal);
|
vstd::erase_if_present(basicGoals, goal);
|
||||||
|
|
||||||
//add abstract goals
|
//add abstract goals
|
||||||
boost::sort(goalsToAdd, [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
|
boost::sort(goalsToAdd, [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
|
||||||
{
|
{
|
||||||
return lhs->priority > rhs->priority; //highest priority at the beginning
|
return lhs->priority > rhs->priority; //highest priority at the beginning
|
||||||
});
|
});
|
||||||
|
|
||||||
//max number of goals = 10
|
//max number of goals = 10
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (basicGoals.size() < 10 && goalsToAdd.size() > i)
|
while (basicGoals.size() < 10 && goalsToAdd.size() > i)
|
||||||
@ -2434,170 +2436,6 @@ Goals::TSubgoal VCAI::decomposeGoal(Goals::TSubgoal ultimateGoal)
|
|||||||
return abstractGoal;
|
return abstractGoal;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TSubgoal VCAI::questToGoal(const QuestInfo & q)
|
|
||||||
{
|
|
||||||
Goals::TSubgoal result = sptr(Goals::Invalid());
|
|
||||||
|
|
||||||
if (q.quest->missionType && q.quest->progress != CQuest::COMPLETE)
|
|
||||||
{
|
|
||||||
MetaString ms;
|
|
||||||
q.quest->getRolloverText(ms, false);
|
|
||||||
logAi->debug("Trying to realize quest: %s", ms.toString());
|
|
||||||
auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
|
|
||||||
|
|
||||||
switch (q.quest->missionType)
|
|
||||||
{
|
|
||||||
case CQuest::MISSION_ART:
|
|
||||||
{
|
|
||||||
for (auto hero : heroes)
|
|
||||||
{
|
|
||||||
if (q.quest->checkQuest(hero))
|
|
||||||
{
|
|
||||||
return sptr(Goals::VisitObj(q.obj->id.getNum()).sethero(hero));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto art : q.quest->m5arts)
|
|
||||||
{
|
|
||||||
return sptr(Goals::GetArtOfType(art)); //TODO: transport?
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_HERO:
|
|
||||||
{
|
|
||||||
//striveToGoal (CGoal(RECRUIT_HERO));
|
|
||||||
for (auto hero : heroes)
|
|
||||||
{
|
|
||||||
if (q.quest->checkQuest(hero))
|
|
||||||
{
|
|
||||||
return sptr(Goals::VisitObj(q.obj->id.getNum()).sethero(hero));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sptr(Goals::FindObj(Obj::PRISON)); //rule of a thumb - quest heroes usually are locked in prisons
|
|
||||||
//BNLOG ("Don't know how to recruit hero with id %d\n", q.quest->m13489val);
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_ARMY:
|
|
||||||
{
|
|
||||||
for (auto hero : heroes)
|
|
||||||
{
|
|
||||||
if (q.quest->checkQuest(hero)) //very bad info - stacks can be split between multiple heroes :(
|
|
||||||
{
|
|
||||||
result = sptr(Goals::VisitObj(q.obj->id.getNum()).sethero(hero));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(result->invalid())
|
|
||||||
{
|
|
||||||
for(auto creature : q.quest->m6creatures)
|
|
||||||
{
|
|
||||||
result = sptr(Goals::GatherTroops(creature.type->idNumber, creature.count));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//TODO: exchange armies... oh my
|
|
||||||
//BNLOG ("Don't know how to recruit %d of %s\n", (int)(creature.count) % creature.type->namePl);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_RESOURCES:
|
|
||||||
{
|
|
||||||
if (heroes.size())
|
|
||||||
{
|
|
||||||
if (q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is
|
|
||||||
{
|
|
||||||
return sptr(Goals::VisitObj(q.obj->id.getNum()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = 0; i < q.quest->m7resources.size(); ++i)
|
|
||||||
{
|
|
||||||
if (q.quest->m7resources[i])
|
|
||||||
return sptr(Goals::CollectRes(i, q.quest->m7resources[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return sptr(Goals::RecruitHero()); //FIXME: checkQuest requires any hero belonging to player :(
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_KILL_HERO:
|
|
||||||
case CQuest::MISSION_KILL_CREATURE:
|
|
||||||
{
|
|
||||||
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
|
|
||||||
|
|
||||||
if(!obj)
|
|
||||||
return sptr(Goals::VisitObj(q.obj->id.getNum())); //visit seer hut
|
|
||||||
|
|
||||||
if(obj->ID == Obj::HERO)
|
|
||||||
{
|
|
||||||
auto relations = myCb->getPlayerRelations(playerID, obj->tempOwner);
|
|
||||||
|
|
||||||
if(relations == PlayerRelations::SAME_PLAYER)
|
|
||||||
{
|
|
||||||
auto heroToProtect = cb->getHero(obj->id);
|
|
||||||
|
|
||||||
return sptr(Goals::GatherArmy().sethero(heroToProtect));
|
|
||||||
}
|
|
||||||
else if(relations == PlayerRelations::ALLIES)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sptr(Goals::VisitObj(obj->id.getNum()));
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_PRIMARY_STAT:
|
|
||||||
{
|
|
||||||
auto heroes = cb->getHeroesInfo();
|
|
||||||
for (auto hero : heroes)
|
|
||||||
{
|
|
||||||
if (q.quest->checkQuest(hero))
|
|
||||||
{
|
|
||||||
return sptr(Goals::VisitObj(q.obj->id.getNum()).sethero(hero));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < q.quest->m2stats.size(); ++i)
|
|
||||||
{
|
|
||||||
logAi->debug("Don't know how to increase primary stat %d", i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_LEVEL:
|
|
||||||
{
|
|
||||||
auto heroes = cb->getHeroesInfo();
|
|
||||||
for (auto hero : heroes)
|
|
||||||
{
|
|
||||||
if (q.quest->checkQuest(hero))
|
|
||||||
{
|
|
||||||
return sptr(Goals::VisitObj(q.obj->id.getNum()).sethero(hero)); //TODO: causes infinite loop :/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logAi->debug("Don't know how to reach hero level %d", q.quest->m13489val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_PLAYER:
|
|
||||||
{
|
|
||||||
if (playerID.getNum() != q.quest->m13489val)
|
|
||||||
logAi->debug("Can't be player of color %d", q.quest->m13489val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CQuest::MISSION_KEYMASTER:
|
|
||||||
{
|
|
||||||
return sptr(Goals::FindObj(Obj::KEYMASTER, q.obj->subID));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} //end of switch
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VCAI::performTypicalActions()
|
void VCAI::performTypicalActions()
|
||||||
{
|
{
|
||||||
for(auto h : getUnblockedHeroes())
|
for(auto h : getUnblockedHeroes())
|
||||||
|
@ -210,7 +210,6 @@ public:
|
|||||||
void setGoal(HeroPtr h, Goals::TSubgoal goal);
|
void setGoal(HeroPtr h, Goals::TSubgoal goal);
|
||||||
void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
|
void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
|
||||||
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
|
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
|
||||||
Goals::TSubgoal questToGoal(const QuestInfo & q);
|
|
||||||
|
|
||||||
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
||||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<uint32_t> movementCostLimit = boost::none);
|
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<uint32_t> movementCostLimit = boost::none);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user