mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
- Fixed unserialized GrowingArtifact, causing crash
- An attempt to teach AI completing quests - Gathering army is now continuous, abstract goal - Lots of fixes and tweaks in AI
This commit is contained in:
parent
b86706d58c
commit
624908e403
214
AI/VCAI/VCAI.cpp
214
AI/VCAI/VCAI.cpp
@ -398,6 +398,11 @@ ui64 evaluateDanger(const CGObjectInstance *obj)
|
||||
const CGDwelling *d = dynamic_cast<const CGDwelling*>(obj);
|
||||
return d->getArmyStrength();
|
||||
}
|
||||
case Obj::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:
|
||||
@ -959,9 +964,10 @@ void VCAI::makeTurn()
|
||||
++dangerousObjects;
|
||||
}
|
||||
}
|
||||
if (dangerousObjects && totalDanger / dangerousObjects > h->getHeroStrength())
|
||||
ui64 averageDanger = totalDanger / dangerousObjects;
|
||||
if (dangerousObjects && averageDanger > h->getHeroStrength())
|
||||
{
|
||||
setGoal (h, CGoal(GATHER_ARMY).sethero(h));
|
||||
setGoal (h, CGoal(GATHER_ARMY).sethero(h).setvalue(averageDanger * SAFE_ATTACK_CONSTANT).setisAbstract(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1025,6 +1031,12 @@ void VCAI::makeTurnInternal()
|
||||
safeCopy.erase(it);
|
||||
}
|
||||
|
||||
auto quests = myCb->getMyQuests();
|
||||
BOOST_FOREACH (auto quest, quests)
|
||||
{
|
||||
striveToQuest (quest);
|
||||
}
|
||||
|
||||
striveToGoal(CGoal(BUILD)); //TODO: smarter building management
|
||||
}
|
||||
catch(boost::thread_interrupted &e)
|
||||
@ -1053,9 +1065,11 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
|
||||
{
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
recruitCreatures (dynamic_cast<const CGDwelling *>(obj));
|
||||
checkHeroArmy (h);
|
||||
break;
|
||||
case GameConstants::TOWNI_TYPE:
|
||||
moveCreaturesToHero (dynamic_cast<const CGTownInstance *>(obj));
|
||||
townVisitsThisWeek[h].push_back(h->visitedTown);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
@ -1102,6 +1116,12 @@ void VCAI::pickBestCreatures(const CArmedInstance * army, const CArmedInstance *
|
||||
cb->mergeOrSwapStacks(armyPtr, army, j, i);
|
||||
|
||||
//TODO - having now strongest possible army, we may want to think about arranging stacks
|
||||
|
||||
auto hero = dynamic_cast<const CGHeroInstance *>(army);
|
||||
if (hero)
|
||||
{
|
||||
checkHeroArmy (hero);
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::recruitCreatures(const CGDwelling * d)
|
||||
@ -1769,7 +1789,7 @@ void VCAI::tryRealize(CGoal g)
|
||||
}
|
||||
}
|
||||
else
|
||||
throw cannotFulfillGoalException("There's a blocked gate!");
|
||||
throw cannotFulfillGoalException("There's a blocked gate!"); //TODO: get keymaster tent
|
||||
}
|
||||
break;
|
||||
case BUILD_STRUCTURE:
|
||||
@ -1817,7 +1837,7 @@ void VCAI::tryRealize(CGoal g)
|
||||
}
|
||||
break;
|
||||
|
||||
case COLLECT_RES:
|
||||
case COLLECT_RES: //TODO: use piles and mines?
|
||||
if(const CGObjectInstance *obj = cb->getObj(g.objid, false))
|
||||
{
|
||||
if(const IMarket *m = IMarket::castFrom(obj, false))
|
||||
@ -1851,7 +1871,7 @@ void VCAI::tryRealize(CGoal g)
|
||||
throw cannotFulfillGoalException("I don't know how to fulfill this!");
|
||||
|
||||
case BUILD:
|
||||
performTypicalActions();
|
||||
performTypicalActions(); //TODO: separate build and wander
|
||||
throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
|
||||
|
||||
case INVALID:
|
||||
@ -1936,6 +1956,7 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
|
||||
catch(std::exception &e)
|
||||
{
|
||||
BNLOG("Goal %s decomposition failed: %s", goalName(goal.goalType) % e.what());
|
||||
//setGoal (goal.hero, INVALID); //test: if we don't know how to realize goal, we should abandon it for now
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -2005,6 +2026,7 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
|
||||
catch(std::exception &e)
|
||||
{
|
||||
BNLOG("Goal %s decomposition failed: %s", goalName(goal.goalType) % e.what());
|
||||
//setGoal (goal.hero, INVALID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -2036,6 +2058,108 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::striveToQuest (const QuestInfo &q)
|
||||
{
|
||||
if (q.quest && q.quest->progress < CQuest::COMPLETE)
|
||||
{
|
||||
MetaString ms;
|
||||
q.quest->getRolloverText(ms, false);
|
||||
BNLOG ("Trying to realize quest: %s\n", ms.toString());
|
||||
switch (q.quest->missionType)
|
||||
{
|
||||
case CQuest::MISSION_ART:
|
||||
{
|
||||
BOOST_FOREACH (auto art, q.quest->m5arts)
|
||||
{
|
||||
striveToGoal (CGoal(GET_ART_TYPE).setaid(art)); //TODO: transport?
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_HERO:
|
||||
{
|
||||
//striveToGoal (CGoal(RECRUIT_HERO));
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
BOOST_FOREACH (auto hero, heroes)
|
||||
{
|
||||
if (q.quest->checkQuest(hero))
|
||||
{
|
||||
striveToGoal (CGoal(GET_OBJ).setobjid(q.obj->id).sethero(hero));
|
||||
break;
|
||||
}
|
||||
}
|
||||
BNLOG ("Don't know how to recruit hero with id %d\n", q.quest->m13489val);
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_ARMY:
|
||||
{
|
||||
BOOST_FOREACH (auto creature, q.quest->m6creatures)
|
||||
{
|
||||
BNLOG ("Don't know how to recruit %d of %s\n", (int)(creature.count) % creature.type->namePl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_RESOURCES:
|
||||
{
|
||||
for (int i = 0; i < q.quest->m7resources.size(); ++i)
|
||||
{
|
||||
if (q.quest->m7resources[i])
|
||||
striveToGoal (CGoal(COLLECT_RES).setresID(i).setvalue(q.quest->m7resources[i]));
|
||||
//TODO: visit object
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_KILL_HERO:
|
||||
case CQuest::MISSION_KILL_CREATURE:
|
||||
{
|
||||
striveToGoal (CGoal(GET_OBJ).setobjid(q.quest->m13489val));
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_PRIMARY_STAT:
|
||||
{
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
BOOST_FOREACH (auto hero, heroes)
|
||||
{
|
||||
if (q.quest->checkQuest(hero))
|
||||
{
|
||||
striveToGoal (CGoal(GET_OBJ).setobjid(q.obj->id).sethero(hero));
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < q.quest->m2stats.size(); ++i)
|
||||
{
|
||||
BNLOG ("Don't know how to increase primary stat %d\n", i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_LEVEL:
|
||||
{
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
BOOST_FOREACH (auto hero, heroes)
|
||||
{
|
||||
if (q.quest->checkQuest(hero))
|
||||
{
|
||||
striveToGoal (CGoal(GET_OBJ).setobjid(q.obj->id).sethero(hero));
|
||||
break;
|
||||
}
|
||||
}
|
||||
BNLOG ("Don't know how to reach hero level %d\n", q.quest->m13489val);
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_PLAYER:
|
||||
{
|
||||
if (playerID != q.quest->m13489val)
|
||||
BNLOG ("Can't be player of color %d\n", q.quest->m13489val);
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_KEYMASTER:
|
||||
{
|
||||
striveToGoal (CGoal(FIND_OBJ).setobjid(Obj::KEYMASTER).setresID(q.quest->m13489val));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::performTypicalActions()
|
||||
{
|
||||
BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo())
|
||||
@ -2188,6 +2312,16 @@ bool VCAI::containsSavedRes(const TResources &cost) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void VCAI::checkHeroArmy (HeroPtr h)
|
||||
{
|
||||
auto it = lockedHeroes.find(h);
|
||||
if (it != lockedHeroes.end())
|
||||
{
|
||||
if (it->second.goalType == GATHER_ARMY && it->second.value <= h->getArmyStrength())
|
||||
completeGoal(CGoal (GATHER_ARMY).sethero(h));
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::recruitHero(const CGTownInstance * t)
|
||||
{
|
||||
BNLOG("Trying to recruit a hero in %s at %s", t->name % t->visitablePos())
|
||||
@ -2477,7 +2611,7 @@ TSubgoal CGoal::whatToDoToAchieve()
|
||||
if(ratio > 0.99)
|
||||
{
|
||||
return CGoal(DIG_AT_TILE).settile(grailPos);
|
||||
}
|
||||
} //TODO: use FIND_OBJ
|
||||
else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
|
||||
{
|
||||
return CGoal(GET_OBJ).setobjid(obj->id);
|
||||
@ -2508,6 +2642,35 @@ TSubgoal CGoal::whatToDoToAchieve()
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FIND_OBJ:
|
||||
{
|
||||
const CGObjectInstance * o = NULL;
|
||||
if (resID > -1) //specified
|
||||
{
|
||||
BOOST_FOREACH(const CGObjectInstance *obj, ai->visitableObjs)
|
||||
{
|
||||
if(obj->ID == objid && obj->subID == resID)
|
||||
{
|
||||
o = obj;
|
||||
break; //TODO: consider multiple objects and choose best
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
BOOST_FOREACH(const CGObjectInstance *obj, ai->visitableObjs)
|
||||
{
|
||||
if(obj->ID == objid)
|
||||
{
|
||||
o = obj;
|
||||
break; //TODO: consider multiple objects and choose best
|
||||
}
|
||||
}
|
||||
if (o)
|
||||
return CGoal(GET_OBJ).setobjid(o->id);
|
||||
else
|
||||
return CGoal(EXPLORE);
|
||||
}
|
||||
break;
|
||||
case GET_OBJ:
|
||||
{
|
||||
const CGObjectInstance * obj = cb->getObj(objid);
|
||||
@ -2519,17 +2682,9 @@ TSubgoal CGoal::whatToDoToAchieve()
|
||||
break;
|
||||
case GET_ART_TYPE:
|
||||
{
|
||||
const CGObjectInstance *artInst = ai->lookForArt(aid);
|
||||
if(!artInst)
|
||||
{
|
||||
TSubgoal alternativeWay = CGoal::lookForArtSmart(aid);
|
||||
if(alternativeWay.invalid())
|
||||
return CGoal(EXPLORE);
|
||||
else
|
||||
return alternativeWay;
|
||||
}
|
||||
else
|
||||
return CGoal(GET_OBJ).setobjid(artInst->id);
|
||||
TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use
|
||||
if(alternativeWay.invalid())
|
||||
return CGoal(FIND_OBJ).setobjid(Obj::ARTIFACT).setresID(aid);
|
||||
}
|
||||
break;
|
||||
case CLEAR_WAY_TO:
|
||||
@ -2571,7 +2726,7 @@ TSubgoal CGoal::whatToDoToAchieve()
|
||||
throw cannotFulfillGoalException(problem);
|
||||
}
|
||||
|
||||
return CGoal(VISIT_TILE).settile(tileToHit).sethero(h);
|
||||
return CGoal(VISIT_TILE).settile(tileToHit).sethero(h); //FIXME:: attempts to visit completely unreachable tile with hero results in stall
|
||||
|
||||
//TODO czy istnieje lepsza droga?
|
||||
|
||||
@ -2665,7 +2820,7 @@ TSubgoal CGoal::whatToDoToAchieve()
|
||||
return CGoal(*this).setisElementar(true);
|
||||
else
|
||||
{
|
||||
return CGoal(GATHER_ARMY).sethero(hero);
|
||||
return CGoal(GATHER_ARMY).sethero(hero).setvalue(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT); //TODO: should it be abstract?
|
||||
}
|
||||
}
|
||||
else //inaccessible for all heroes
|
||||
@ -2837,7 +2992,7 @@ TSubgoal CGoal::whatToDoToAchieve()
|
||||
{
|
||||
if(!t->visitingHero && howManyReinforcementsCanGet(hero,t))
|
||||
{
|
||||
if(isReachable(t))
|
||||
if(isReachable(t) && !vstd::contains (ai->townVisitsThisWeek[hero], t))
|
||||
townsReachable.push_back(t);
|
||||
}
|
||||
}
|
||||
@ -3030,6 +3185,10 @@ bool isWeeklyRevisitable (const CGObjectInstance * obj)
|
||||
{
|
||||
case Obj::STABLES: //any other potential visitable objects?
|
||||
return true;
|
||||
case Obj::BORDER_GATE:
|
||||
case Obj::BORDERGUARD:
|
||||
return (dynamic_cast <const CGKeys *>(obj))->wasMyColorVisited (ai->playerID); //FIXME: they could be revisited sooner than in a week
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -3038,6 +3197,21 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||
{
|
||||
switch (obj->ID)
|
||||
{
|
||||
case Obj::SEER_HUT:
|
||||
{
|
||||
BOOST_FOREACH (auto q, ai->myCb->getMyQuests())
|
||||
{
|
||||
if (q.obj = obj)
|
||||
{
|
||||
if (q.quest->checkQuest(*h))
|
||||
return true; //we completed the quest
|
||||
else
|
||||
return false; //we can't complete this quest
|
||||
}
|
||||
return true; //we don't have this quest yet
|
||||
|
||||
}
|
||||
}
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
{
|
||||
if (obj->tempOwner != h->tempOwner)
|
||||
|
@ -76,6 +76,7 @@ enum EGoals
|
||||
|
||||
OBJECT_GOALS_BEGIN,
|
||||
GET_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)
|
||||
|
||||
GET_ART_TYPE,
|
||||
|
||||
@ -115,8 +116,10 @@ struct CGoal
|
||||
priority = 0;
|
||||
isElementar = false;
|
||||
isAbstract = false;
|
||||
value = 0;
|
||||
objid = -1;
|
||||
aid = -1;
|
||||
resID = -1;
|
||||
tile = int3(-1, -1, -1);
|
||||
town = NULL;
|
||||
}
|
||||
@ -319,6 +322,7 @@ public:
|
||||
void setGoal(HeroPtr h, const CGoal goal);
|
||||
void setGoal(HeroPtr h, EGoals goalType = INVALID);
|
||||
void completeGoal (const CGoal goal); //safely removes goal from reserved hero
|
||||
void striveToQuest (const QuestInfo &q);
|
||||
|
||||
void recruitHero(const CGTownInstance * t);
|
||||
std::vector<const CGObjectInstance *> getPossibleDestinations(HeroPtr h);
|
||||
@ -356,6 +360,7 @@ public:
|
||||
HeroPtr primaryHero() const;
|
||||
TResources estimateIncome() const;
|
||||
bool containsSavedRes(const TResources &cost) const;
|
||||
void checkHeroArmy (HeroPtr h);
|
||||
|
||||
void requestSent(const CPackForServer *pack, int requestID) OVERRIDE;
|
||||
void answerQuery(int queryID, int selection);
|
||||
|
@ -4539,10 +4539,10 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
|
||||
if (firstVisit)
|
||||
{
|
||||
isCustom = isCustomFirst;
|
||||
cb->setObjProperty (id, 10, 1);
|
||||
cb->setObjProperty (id, 10, IN_PROGRESS);
|
||||
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (this, this, pos);
|
||||
aq.quest = QuestInfo (this, this, visitablePos());
|
||||
aq.player = h->tempOwner;
|
||||
cb->sendAndApply (&aq); //TODO: merge with setObjProperty?
|
||||
}
|
||||
@ -4632,7 +4632,8 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cb->setObjProperty(id,11,0); //no more mission available
|
||||
cb->setObjProperty (id, 10, COMPLETE); //mission complete - for AI
|
||||
cb->setObjProperty (id, 11, 0); //no more mission available - redundant?
|
||||
completeQuest(h); //make sure to remove QuestQuard at the very end
|
||||
}
|
||||
}
|
||||
@ -6344,7 +6345,7 @@ void CGBorderGuard::onHeroVisit( const CGHeroInstance * h ) const
|
||||
cb->showInfoDialog (&iw);
|
||||
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (this, this, pos);
|
||||
aq.quest = QuestInfo (this, this, visitablePos());
|
||||
aq.player = h->tempOwner;
|
||||
cb->sendAndApply (&aq);
|
||||
//TODO: add this quest only once OR check for multiple instances later
|
||||
@ -6367,7 +6368,7 @@ void CGBorderGate::onHeroVisit( const CGHeroInstance * h ) const //TODO: passabi
|
||||
cb->showInfoDialog(&iw);
|
||||
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (this, this, pos);
|
||||
aq.quest = QuestInfo (this, this, visitablePos());
|
||||
aq.player = h->tempOwner;
|
||||
cb->sendAndApply (&aq);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ class DLL_LINKAGE CQuest
|
||||
public:
|
||||
enum Emission {MISSION_NONE = 0, MISSION_LEVEL = 1, MISSION_PRIMARY_STAT = 2, MISSION_KILL_HERO = 3, MISSION_KILL_CREATURE = 4,
|
||||
MISSION_ART = 5, MISSION_ARMY = 6, MISSION_RESOURCES = 7, MISSION_HERO = 8, MISSION_PLAYER = 9, MISSION_KEYMASTER = 10};
|
||||
enum Eprogress {NOT_ACTIVE, IN_PROGRESS, COMPLETE};
|
||||
|
||||
si32 qid; //unique quets id for serialization / identification
|
||||
|
||||
@ -69,7 +70,7 @@ public:
|
||||
std::vector<ui32> m2stats;
|
||||
std::vector<ui16> m5arts; //artifacts id
|
||||
std::vector<CStackBasicDescriptor> m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
|
||||
std::vector<ui32> m7resources;
|
||||
std::vector<ui32> m7resources; //TODO: use resourceset?
|
||||
|
||||
//following field are used only for kill creature/hero missions, the original objects became inaccessible after their removal, so we need to store info needed for messages / hover text
|
||||
ui8 textOption;
|
||||
|
@ -185,7 +185,9 @@ namespace Obj
|
||||
{
|
||||
enum
|
||||
{
|
||||
ARTIFACT = 5,
|
||||
BOAT = 8,
|
||||
BORDERGUARD = 9,
|
||||
KEYMASTER = 10,
|
||||
CREATURE_BANK = 16,
|
||||
CREATURE_GENERATOR1 = 17,
|
||||
@ -203,6 +205,7 @@ namespace Obj
|
||||
MONSTER = 54,
|
||||
OBELISK = 57,
|
||||
PYRAMID = 63,
|
||||
SEER_HUT = 83,
|
||||
CRYPT = 84,
|
||||
SHIPWRECK = 85,
|
||||
STABLES = 94,
|
||||
|
@ -84,6 +84,7 @@ void registerTypes1(Serializer &s)
|
||||
|
||||
s.template registerType<CBonusSystemNode>();
|
||||
s.template registerType<CArtifact>();
|
||||
s.template registerType<CGrowingArtifact>();
|
||||
s.template registerType<CCreature>();
|
||||
s.template registerType<CStackInstance>();
|
||||
s.template registerType<CCommanderInstance>();
|
||||
|
Loading…
Reference in New Issue
Block a user