Переглянути джерело

Fixed #1430, work-around to make #1435 non-crashing.
Fixed at least one of the #1428 infinite turns cases.

Michał W. Urbańczyk 12 роки тому
батько
коміт
b62bb096a7
5 змінених файлів з 42 додано та 11 видалено
  1. 22 8
      AI/VCAI/VCAI.cpp
  2. 1 0
      AI/VCAI/VCAI.h
  3. 9 0
      Global.h
  4. 6 3
      client/battle/CBattleInterface.cpp
  5. 4 0
      lib/ConstTransitivePtr.h

+ 22 - 8
AI/VCAI/VCAI.cpp

@@ -1228,9 +1228,7 @@ void VCAI::recruitCreatures(const CGDwelling * d)
 // 		if(containsSavedRes(c->cost))
 // 		if(containsSavedRes(c->cost))
 // 			continue;
 // 			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)
 		if(count > 0)
 			cb->recruitCreatures(d, creID, count, i);
 			cb->recruitCreatures(d, creID, count, i);
 	}
 	}
@@ -1900,6 +1898,11 @@ void VCAI::tryRealize(CGoal g)
 			//cb->recalculatePaths();
 			//cb->recalculatePaths();
 			if(!g.hero->movement)
 			if(!g.hero->movement)
 				throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
 				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(!g.isBlockedBorderGate(g.tile))
 			//{
 			//{
 				if (ai->moveHeroToTile(g.tile, g.hero.get()))
 				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()
 AIStatus::AIStatus()
 {
 {
 	battle = NO_BATTLE;
 	battle = NO_BATTLE;
@@ -3264,7 +3275,7 @@ TSubgoal CGoal::whatToDoToAchieve()
 					{
 					{
 						for (auto type : creature.second)
 						for (auto type : creature.second)
 						{
 						{
-							if (type == objid)
+							if (type == objid  &&  ai->freeResources().canAfford(VLC->creh->creatures[type]->cost))
 								dwellings.push_back(d);
 								dwellings.push_back(d);
 						}
 						}
 					}
 					}
@@ -3273,7 +3284,7 @@ TSubgoal CGoal::whatToDoToAchieve()
 			if (dwellings.size())
 			if (dwellings.size())
 			{
 			{
 				boost::sort(dwellings, isCloser);
 				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
 			else
 				return CGoal(EXPLORE);
 				return CGoal(EXPLORE);
@@ -3435,9 +3446,12 @@ TSubgoal CGoal::whatToDoToAchieve()
 						{
 						{
 							if(creLevel.first)
 							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;
+								}
 							}
 							}
 						}
 						}
 					}
 					}

+ 1 - 0
AI/VCAI/VCAI.h

@@ -420,6 +420,7 @@ public:
 
 
 	std::vector<HeroPtr> getUnblockedHeroes() const;
 	std::vector<HeroPtr> getUnblockedHeroes() const;
 	HeroPtr primaryHero() const;
 	HeroPtr primaryHero() const;
+	TResources freeResources() const; //owned resources minus gold reserve
 	TResources estimateIncome() const;
 	TResources estimateIncome() const;
 	bool containsSavedRes(const TResources &cost) const;
 	bool containsSavedRes(const TResources &cost) const;
 	void checkHeroArmy (HeroPtr h);
 	void checkHeroArmy (HeroPtr h);

+ 9 - 0
Global.h

@@ -629,6 +629,15 @@ namespace vstd
 			return *pos;
 			return *pos;
 	}
 	}
 
 
+	template <typename Container>
+	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 boost::math::round;
 }
 }
 using vstd::operator-=;
 using vstd::operator-=;

+ 6 - 3
client/battle/CBattleInterface.cpp

@@ -1431,7 +1431,10 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 					break;
 					break;
 				default:
 				default:
 					text = CGI->generaltexth->allTexts[565]; //The %s casts %s
 					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)
 			if (plural)
 			{
 			{
@@ -1459,9 +1462,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 		{
 		{
 			boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetHeroInfo(sc->side).name);
 			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
 		else
 		{
 		{

+ 4 - 0
lib/ConstTransitivePtr.h

@@ -23,6 +23,10 @@ public:
 	ConstTransitivePtr(T *Ptr = nullptr)
 	ConstTransitivePtr(T *Ptr = nullptr)
 		: ptr(Ptr) 
 		: ptr(Ptr) 
 	{}
 	{}
+	ConstTransitivePtr(std::nullptr_t)
+		: ptr(nullptr) 
+	{}
+
 
 
 	const T& operator*() const
 	const T& operator*() const
 	{
 	{