1
0
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:
DjWarmonger 2012-07-18 10:10:14 +00:00
parent b86706d58c
commit 624908e403
6 changed files with 211 additions and 26 deletions

View File

@ -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);
TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use
if(alternativeWay.invalid())
return CGoal(EXPLORE);
else
return alternativeWay;
}
else
return CGoal(GET_OBJ).setobjid(artInst->id);
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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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,

View File

@ -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>();