From a3cad2883f59b9dcd9419c7e88de3226008cbd02 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Mon, 17 Feb 2014 17:28:39 +0000 Subject: [PATCH] - Restored correct specialty serialization (#1599 and all its children) - Fixed rare AI crash - Fixed AI visiting some objects many times - Some cleanup and refactoring --- AI/VCAI/AIUtility.cpp | 2 +- AI/VCAI/Fuzzy.cpp | 2 ++ AI/VCAI/Goals.cpp | 53 ++++++++---------------------------------- AI/VCAI/VCAI.cpp | 47 +++++++++++++++++++++++++++++++------ AI/VCAI/VCAI.h | 5 ++-- lib/CObjectHandler.cpp | 8 +++---- lib/IGameCallback.cpp | 4 ++++ lib/IGameCallback.h | 1 + lib/mapping/CMap.cpp | 5 ++++ lib/mapping/CMap.h | 1 + 10 files changed, 71 insertions(+), 57 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 08f2092c2..a43813eb3 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -347,7 +347,7 @@ int3 whereToExplore(HeroPtr h) catch(cannotFulfillGoalException &e) { //perform exhaustive search - return ai->explorationNewPoint(radius, h); + return ai->explorationNewPoint(h); } } diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index d3d272002..2aae4bcc6 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -467,6 +467,8 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g) float FuzzyHelper::evaluate (Goals::VisitHero & g) { auto obj = cb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similiar + if (!obj) + return -100; //hero died in the meantime //TODO: consider direct copy (constructor?) g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).setisAbstract(g.isAbstract).accept(this)); return g.priority; diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index 02eb38657..b767a2cfc 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -418,7 +418,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals() ret.push_back (sptr (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID))); } - auto topObj = backOrNull(cb->getVisitableObjs(tileToHit)); + auto topObj = cb->getTopObj(tileToHit); if(topObj) { if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) @@ -516,18 +516,7 @@ TGoalVec Explore::getAllPossibleSubgoals() for (auto obj : objs) //double loop, performance risk? { auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded - if (t.valid()) - { - if (isSafeToVisit(h, t)) - { - ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); - } - else - { - ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT). - sethero(h).setisAbstract(true))); - } - } + ai->whatToDoToReachTile(h, t, ret); } int3 t = whereToExplore(h); @@ -537,19 +526,8 @@ TGoalVec Explore::getAllPossibleSubgoals() } else if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost { - t = ai->explorationDesperate(h->getSightRadious(), h); - if (t.valid()) - { - if (isSafeToVisit(h, t)) - { - ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); - } - else - { - ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT). - sethero(h).setisAbstract(true))); - } - } + t = ai->explorationDesperate(h); + ai->whatToDoToReachTile(h, t, ret); } } //we either don't have hero yet or none of heroes can explore @@ -560,7 +538,7 @@ TGoalVec Explore::getAllPossibleSubgoals() //{ // for (auto h : heroes) //this is costly function, use only when there is no other way // { - // auto t = ai->explorationDesperate (h->getSightRadious(), h); //we assume that no more than one tile on the way is guarded + // auto t = ai->explorationDesperate (h); //we assume that no more than one tile on the way is guarded // if (t.valid()) // { // if (isSafeToVisit(h, t)) @@ -744,7 +722,7 @@ TSubgoal CollectRes::whatToDoToAchieve() if(howManyCanWeBuy + cb->getResourceAmount(static_cast(resID)) >= value) { - auto backObj = backOrNull(cb->getVisitableObjs(m->o->visitablePos())); //it'll be a hero if we have one there; otherwise marketplace + auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace assert(backObj); if (backObj->tempOwner != ai->playerID) { @@ -858,21 +836,7 @@ TGoalVec Conquer::getAllPossibleSubgoals() for (auto obj : ourObjs) //double loop, performance risk? { auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded - if (t.valid()) - { - if (isSafeToVisit(h, t)) - { - if (obj->ID == Obj::HERO) - ret.push_back (sptr (Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true))); - //track enemy hero - else - ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); - } - else - { - ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t,h)*SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true))); - } - } + ai->whatToDoToReachTile (h, t, ret); } } if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture @@ -978,6 +942,9 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() ret.push_back (sptr (Goals::VisitTile(pos).sethero(h))); } } + if (ai->canRecruitAnyHero()) //this is not stupid in early phase of game + ret.push_back (sptr(Goals::RecruitHero())); + if (ret.empty()) { if (hero == ai->primaryHero() || value >= 1.1f) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index e2fdef058..e368daaf5 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -742,7 +742,6 @@ void VCAI::makeTurnInternal() std::vector > safeCopy; for (auto mission : lockedHeroes) { - //FIXME: for some reason when called here, priority is always the same fh->setPriority (mission.second); //re-evaluate if (canAct(mission.first)) { @@ -1241,6 +1240,26 @@ std::vector VCAI::getPossibleDestinations(HeroPtr h) return possibleDestinations; } +void VCAI::whatToDoToReachTile (const CGHeroInstance * h, int3 t, Goals::TGoalVec& vec) +///TODO: possibly merge with Goals::ClearWayTo +{ + if (t.valid()) + { + auto obj = cb->getTopObj(t); + if (obj && vstd::contains(ai->reservedObjs, obj) && !vstd::contains(reservedHeroesMap[h], obj)) + return; //do not capture object reserved by another hero + if (isSafeToVisit(h, t)) + { + vec.push_back (sptr (Goals::VisitTile(t).sethero(h))); + } + else + { + vec.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT). + sethero(h).setisAbstract(true))); + } + } +} + bool VCAI::canRecruitAnyHero (const CGTownInstance * t) const { //TODO: make gathering gold, building tavern or conquering town (?) possible subgoals @@ -2205,10 +2224,11 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) throw cannotFulfillGoalException("No neighbour will bring new discoveries!"); } -int3 VCAI::explorationNewPoint(int radius, HeroPtr h) +int3 VCAI::explorationNewPoint(HeroPtr h) { //logAi->debugStream() << "Looking for an another place for exploration..."; cb->setSelection(h.h); + int radius = h->getSightRadious(); std::vector > tiles; //tiles[distance_to_fow] tiles.resize(radius); @@ -2288,10 +2308,11 @@ int3 VCAI::explorationNewPoint(int radius, HeroPtr h) return bestTile; } -int3 VCAI::explorationDesperate(int radius, HeroPtr h) +int3 VCAI::explorationDesperate(HeroPtr h) { //logAi->debugStream() << "Looking for an another place for exploration..."; SectorMap sm(h); + int radius = h->getSightRadious(); std::vector > tiles; //tiles[distance_to_fow] tiles.resize(radius); @@ -3049,7 +3070,7 @@ For ship construction etc, another function (goal?) is needed int3 curtile = dst; while(curtile != h->visitablePos()) { - auto topObj = backOrNull(cb->getVisitableObjs(curtile)); + auto topObj = cb->getTopObj(curtile); if (topObj && topObj->ID == Obj::HERO && topObj != h.h) { logAi->warnStream() << ("Another allied hero stands in our way"); @@ -3095,8 +3116,7 @@ void SectorMap::makeParentBFS(crint3 source) ui8 &sec = retreiveTile(curPos); assert(sec == mySector); //consider only tiles from the same sector UNUSED(sec); - - //const TerrainTile *t = cb->getTile(curPos); + foreach_neighbour(curPos, [&](crint3 neighPos) { if(retreiveTile(neighPos) == mySector && !vstd::contains(parent, neighPos)) @@ -3107,7 +3127,20 @@ void SectorMap::makeParentBFS(crint3 source) parent[neighPos] = curPos; } } - }); + }); + //this code is unused, as tiles on both sides of game are different sectors + + //const TerrainTile *t = cb->getTile(curPos); + //if(t->topVisitableId() == Obj::SUBTERRANEAN_GATE) + //{ + // //try finding the exit gate + // auto it = ai->knownSubterraneanGates.find(t->topVisitableObj()); + // if (it != ai->knownSubterraneanGates.end()) + // { + // const int3 outPos = it->second->visitablePos(); + // parent[outPos] = curPos; //TODO: is it only one tile? + // } + //} } } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index b15a69b0f..e252efa1c 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -178,8 +178,9 @@ public: void tryRealize(Goals::AbstractGoal & g); int3 explorationBestNeighbour(int3 hpos, int radius, HeroPtr h); - int3 explorationNewPoint(int radius, HeroPtr h); - int3 explorationDesperate(int radius, HeroPtr h); + int3 explorationNewPoint(HeroPtr h); + int3 explorationDesperate(HeroPtr h); + void whatToDoToReachTile (const CGHeroInstance * h, int3 t, Goals::TGoalVec& vec); void recruitHero(); virtual std::string getBattleAIName() const override; diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 85eaff154..2e2806637 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -1567,10 +1567,10 @@ void CGHeroInstance::deserializationFix() { artDeserializationFix(this); - //for (auto hs : specialty) - //{ - // attachTo (hs); - //} + for (auto hs : specialty) + { + attachTo (hs); + } } CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs) diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 23e10d4a4..654e5b494 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -521,6 +521,10 @@ std::vector CGameInfoCallback::getVisitableObjs(int3 return ret; } +const CGObjectInstance * CGameInfoCallback::getTopObj (int3 pos) const +{ + return vstd::backOrNull(getVisitableObjs(pos)); +} std::vector < const CGObjectInstance * > CGameInfoCallback::getFlaggableObjects(int3 pos) const { diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index d417a72f5..6964594fa 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -111,6 +111,7 @@ public: std::vector getBlockingObjs(int3 pos)const; std::vector getVisitableObjs(int3 pos, bool verbose = true)const; std::vector getFlaggableObjects(int3 pos) const; + const CGObjectInstance * getTopObj (int3 pos) const; std::vector getObjDescriptions(int3 pos)const; //returns descriptions of objects at pos in order from the lowest to the highest PlayerColor getOwner(ObjectInstanceID heroID) const; const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed) diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 77703290e..c61748911 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -132,6 +132,11 @@ int TerrainTile::topVisitableId() const return visitableObjects.size() ? visitableObjects.back()->ID : -1; } +CGObjectInstance * TerrainTile::topVisitableObj() const +{ + return visitableObjects.size() ? visitableObjects.back() : nullptr; +} + bool TerrainTile::isCoastal() const { return extTileFlags & 64; diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 55ccfb016..eb2743c0f 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -288,6 +288,7 @@ struct DLL_LINKAGE TerrainTile bool isClear(const TerrainTile * from = nullptr) const; /// Gets the ID of the top visitable object or -1 if there is none. int topVisitableId() const; + CGObjectInstance * topVisitableObj() const; bool isWater() const; bool isCoastal() const; bool hasFavourableWinds() const;