diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index f50546b0f..99ca58145 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1228,9 +1228,7 @@ void VCAI::recruitCreatures(const CGDwelling * d) // if(containsSavedRes(c->cost)) // continue; - TResources myRes = cb->getResourceAmount(); - myRes[Res::GOLD] -= GOLD_RESERVE; - amin(count, myRes / VLC->creh->creatures[creID]->cost); + amin(count, freeResources() / VLC->creh->creatures[creID]->cost); if(count > 0) cb->recruitCreatures(d, creID, count, i); } @@ -1900,6 +1898,11 @@ void VCAI::tryRealize(CGoal g) //cb->recalculatePaths(); if(!g.hero->movement) throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!"); + if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) + { + logAi->warnStream() << boost::format("Why do I want to move hero %s to tile %s? Already standing on that tile! ") % g.hero->name % g.tile; + throw goalFulfilledException (g); + } //if(!g.isBlockedBorderGate(g.tile)) //{ if (ai->moveHeroToTile(g.tile, g.hero.get())) @@ -2614,6 +2617,14 @@ void VCAI::validateObject(ObjectIdRef obj) } } +TResources VCAI::freeResources() const +{ + TResources myRes = cb->getResourceAmount(); + myRes[Res::GOLD] -= GOLD_RESERVE; + vstd::amax(myRes[Res::GOLD], 0); + return myRes; +} + AIStatus::AIStatus() { battle = NO_BATTLE; @@ -3264,7 +3275,7 @@ TSubgoal CGoal::whatToDoToAchieve() { for (auto type : creature.second) { - if (type == objid) + if (type == objid && ai->freeResources().canAfford(VLC->creh->creatures[type]->cost)) dwellings.push_back(d); } } @@ -3273,7 +3284,7 @@ TSubgoal CGoal::whatToDoToAchieve() if (dwellings.size()) { boost::sort(dwellings, isCloser); - return CGoal(GET_OBJ).setobjid (dwellings.front()->id.getNum()); //TODO: consider needed resources + return CGoal(GET_OBJ).setobjid (dwellings.front()->id.getNum()); } else return CGoal(EXPLORE); @@ -3435,9 +3446,12 @@ TSubgoal CGoal::whatToDoToAchieve() { if(creLevel.first) { - auto creature = VLC->creh->creatures[creLevel.second.front()]; - if(cb->getResourceAmount().canAfford(creature->cost)) - return false; + for(auto & creatureID : creLevel.second) + { + auto creature = VLC->creh->creatures[creatureID]; + if(ai->freeResources().canAfford(creature->cost)) + return false; + } } } } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 81be48cd9..6f6ed9c4f 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -420,6 +420,7 @@ public: std::vector getUnblockedHeroes() const; HeroPtr primaryHero() const; + TResources freeResources() const; //owned resources minus gold reserve TResources estimateIncome() const; bool containsSavedRes(const TResources &cost) const; void checkHeroArmy (HeroPtr h); diff --git a/Global.h b/Global.h index 10190d2c7..5123efcaf 100644 --- a/Global.h +++ b/Global.h @@ -629,6 +629,15 @@ namespace vstd return *pos; } + template + typename Container::const_reference atOrDefault(const Container &r, size_t index, const typename Container::const_reference &defaultValue) + { + if(isValidIndex(r, index)) + return r[index]; + + return defaultValue; + } + using boost::math::round; } using vstd::operator-=; diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 6aeb2f634..ffe56d958 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1431,7 +1431,10 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) break; default: text = CGI->generaltexth->allTexts[565]; //The %s casts %s - boost::algorithm::replace_first(text, "%s", CGI->creh->creatures[sc->attackerType]->namePl); //casting stack + if(auto castingCreature = vstd::atOrDefault(CGI->creh->creatures, sc->attackerType, nullptr)) + boost::algorithm::replace_first(text, "%s", castingCreature->namePl); //casting stack + else + boost::algorithm::replace_first(text, "%s", "@Unknown caster@"); //should not happen } if (plural) { @@ -1459,9 +1462,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) { boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetHeroInfo(sc->side).name); } - else if(sc->attackerType < CGI->creh->creatures.size()) + if(auto castingCreature = vstd::atOrDefault(CGI->creh->creatures, sc->attackerType, nullptr)) { - boost::algorithm::replace_first(text, "%s", CGI->creh->creatures[sc->attackerType]->namePl); //creature caster + boost::algorithm::replace_first(text, "%s", castingCreature->namePl); //creature caster } else { diff --git a/lib/ConstTransitivePtr.h b/lib/ConstTransitivePtr.h index 7391c3f9a..724a54a0d 100644 --- a/lib/ConstTransitivePtr.h +++ b/lib/ConstTransitivePtr.h @@ -23,6 +23,10 @@ public: ConstTransitivePtr(T *Ptr = nullptr) : ptr(Ptr) {} + ConstTransitivePtr(std::nullptr_t) + : ptr(nullptr) + {} + const T& operator*() const {