From 624908e4039bfa15a0222d28dae2c08cd73ad4ab Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Wed, 18 Jul 2012 10:10:14 +0000 Subject: [PATCH] - 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 --- AI/VCAI/VCAI.cpp | 214 +++++++++++++++++++++++++++++++++++++---- AI/VCAI/VCAI.h | 5 + lib/CObjectHandler.cpp | 11 ++- lib/CObjectHandler.h | 3 +- lib/GameConstants.h | 3 + lib/RegisterTypes.h | 1 + 6 files changed, 211 insertions(+), 26 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 58d5ee9e2..4daef864c 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -398,6 +398,11 @@ ui64 evaluateDanger(const CGObjectInstance *obj) const CGDwelling *d = dynamic_cast(obj); return d->getArmyStrength(); } + case Obj::MINE: + { + const CArmedInstance * a = dynamic_cast(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(obj)); + checkHeroArmy (h); break; case GameConstants::TOWNI_TYPE: moveCreaturesToHero (dynamic_cast(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(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)) //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 (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) diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 1375fb975..1671acbd1 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -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 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); diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 0b47b0a12..101628e29 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -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); } diff --git a/lib/CObjectHandler.h b/lib/CObjectHandler.h index f83d64514..192121787 100644 --- a/lib/CObjectHandler.h +++ b/lib/CObjectHandler.h @@ -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 m2stats; std::vector m5arts; //artifacts id std::vector m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant - std::vector m7resources; + std::vector 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; diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 6b11fc6cb..63056e4d5 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -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, diff --git a/lib/RegisterTypes.h b/lib/RegisterTypes.h index bd81af0f6..a4cde2668 100644 --- a/lib/RegisterTypes.h +++ b/lib/RegisterTypes.h @@ -84,6 +84,7 @@ void registerTypes1(Serializer &s) s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType();