Beispiel #1
0
TGoalVec CompleteQuest::missionDestroyObj() const
{
	TGoalVec solutions;

	auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);

	if(!obj)
		return ai->ah->howToVisitObj(q.obj);

	if(obj->ID == Obj::HERO)
	{
		auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);

		if(relations == PlayerRelations::SAME_PLAYER)
		{
			auto heroToProtect = cb->getHero(obj->id);

			solutions.push_back(sptr(GatherArmy().sethero(heroToProtect)));
		}
		else if(relations == PlayerRelations::ENEMIES)
		{
			solutions = ai->ah->howToVisitObj(obj);
		}
	}

	return solutions;
}
Beispiel #2
0
TGoalVec CompleteQuest::missionResources() const
{
	TGoalVec solutions;

	auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?

	if(heroes.size())
	{
		if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is
		{
			return ai->ah->howToVisitObj(q.obj);
		}
		else
		{
			for(int i = 0; i < q.quest->m7resources.size(); ++i)
			{
				if(q.quest->m7resources[i])
					solutions.push_back(sptr(CollectRes(i, q.quest->m7resources[i])));
			}
		}
	}
	else
	{
		solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :(
	}

	return solutions;
}
Beispiel #3
0
TGoalVec CompleteQuest::missionKeymaster() const
{
	TGoalVec solutions = tryCompleteQuest();

	if(solutions.empty())
	{
		solutions.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, q.obj->subID)));
	}

	return solutions;
}
Beispiel #4
0
TGoalVec CompleteQuest::missionLevel() const
{
	TGoalVec solutions = tryCompleteQuest();

	if(solutions.empty())
	{
		logAi->debug("Don't know how to reach hero level %d", q.quest->m13489val);
	}

	return solutions;
}
Beispiel #5
0
TGoalVec CompleteQuest::missionHero() const
{
	TGoalVec solutions = tryCompleteQuest();

	if(solutions.empty())
	{
		//rule of a thumb - quest heroes usually are locked in prisons
		solutions.push_back(sptr(FindObj(Obj::PRISON)));
	}

	return solutions;
}
Beispiel #6
0
TGoalVec Conquer::getAllPossibleSubgoals()
{
	TGoalVec ret;

	auto conquerable = [](const CGObjectInstance * obj) -> bool
	{
		if (cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
		{
			switch (obj->ID.num)
			{
				case Obj::TOWN:
				case Obj::HERO:
				case Obj::CREATURE_GENERATOR1:
				case Obj::MINE: //TODO: check ai->knownSubterraneanGates
					return true;
			}
		}
		return false;
	};

	std::vector<const CGObjectInstance *> objs;
	for (auto obj : ai->visitableObjs)
	{
		if (conquerable(obj)) 
			objs.push_back (obj);
	}

	for (auto h : cb->getHeroesInfo())
	{
		SectorMap sm(h);
		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects

		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
		{
			if (conquerable(obj))
				ourObjs.push_back(obj);
		}
		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 (ai->canReachTile(h, t))
				ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
		}
	}
	if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
		ret.push_back (sptr(Goals::RecruitHero()));

	if (ret.empty())
		ret.push_back (sptr(Goals::Explore())); //we need to find an enemy
	return ret;
}
Beispiel #7
0
TGoalVec Build::getAllPossibleSubgoals()
{
	TGoalVec ret;

	for(const CGTownInstance * t : cb->getTownsInfo())
	{
		//start fresh with every town
		ai->ah->getBuildingOptions(t);
		auto immediateBuilding = ai->ah->immediateBuilding();
		auto expensiveBuilding = ai->ah->expensiveBuilding();

		//handling for early town development to save money and focus on income
		if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.is_initialized())
		{
			auto potentialBuilding = expensiveBuilding.get();
			switch(expensiveBuilding.get().bid)
			{
			case BuildingID::TOWN_HALL:
			case BuildingID::CITY_HALL:
			case BuildingID::CAPITOL:
			case BuildingID::FORT:
			case BuildingID::CITADEL:
			case BuildingID::CASTLE:
				//If above buildings are next to be bought, but no money... do not buy anything else, try to gather resources for these. Simple but has to suffice for now.
				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(2.25)));
				ret.push_back(goal);
				return ret;
				break;
			}
		}

		if(immediateBuilding.is_initialized())
		{
			ret.push_back(sptr(BuildThis(immediateBuilding.get().bid, t).setpriority(2))); //prioritize buildings we can build quick
		}
		else //try build later
		{
			if(expensiveBuilding.is_initialized())
			{
				auto potentialBuilding = expensiveBuilding.get(); //gather resources for any we can't afford
				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(0.5)));
				ret.push_back(goal);
			}
		}
	}

	if(ret.empty())
		throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
	else
		return ret;
}
Beispiel #8
0
TGoalVec CompleteQuest::missionArmy() const
{
	TGoalVec solutions = tryCompleteQuest();

	if(!solutions.empty())
		return solutions;

	for(auto creature : q.quest->m6creatures)
	{
		solutions.push_back(sptr(GatherTroops(creature.type->idNumber, creature.count)));
	}

	return solutions;
}
Beispiel #9
0
TGoalVec CompleteQuest::missionArt() const
{
	TGoalVec solutions = tryCompleteQuest();

	if(!solutions.empty())
		return solutions;

	for(auto art : q.quest->m5arts)
	{
		solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport?
	}

	return solutions;
}
Beispiel #10
0
TGoalVec CompleteQuest::missionIncreasePrimaryStat() const
{
	TGoalVec solutions = tryCompleteQuest();

	if(solutions.empty())
	{
		for(int i = 0; i < q.quest->m2stats.size(); ++i)
		{
			// TODO: library, school and other boost objects
			logAi->debug("Don't know how to increase primary stat %d", i);
		}
	}

	return solutions;
}
Beispiel #11
0
TGoalVec VisitTile::getAllPossibleSubgoals()
{
	assert(cb->isInTheMap(tile));

	TGoalVec ret;
	if (!cb->isVisible(tile))
		ret.push_back (sptr(Goals::Explore())); //what sense does it make?
	else
	{
		std::vector<const CGHeroInstance *> heroes;
		if (hero)
			heroes.push_back(hero.h); //use assigned hero if any
		else
			heroes = cb->getHeroesInfo(); //use most convenient hero

		for (auto h : heroes)
		{
			if (ai->isAccessibleForHero(tile, h))
				ret.push_back (sptr(Goals::VisitTile(tile).sethero(h)));
		}
		if (ai->canRecruitAnyHero())
			ret.push_back (sptr(Goals::RecruitHero()));
	}
	if (ret.empty())
	{
		auto obj = frontOrNull(cb->getVisitableObjs(tile));
		if (obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
			ret.push_back (sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
		else
			ret.push_back (sptr(Goals::ClearWayTo(tile)));
	}

	//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
	return ret;
}
Beispiel #12
0
TSubgoal CompleteQuest::whatToDoToAchieve()
{
	if(q.quest->missionType == CQuest::MISSION_NONE)
	{
		throw cannotFulfillGoalException("Can not complete inactive quest");
	}

	TGoalVec solutions = getAllPossibleSubgoals();

	if(solutions.empty())
		throw cannotFulfillGoalException("Can not complete quest " + questToString());

	TSubgoal result = fh->chooseSolution(solutions);

	logAi->trace(
		"Returning %s, tile: %s, objid: %d, hero: %s",
		result->name(),
		result->tile.toString(),
		result->objid,
		result->hero.validAndSet() ? result->hero->name : "not specified");

	return result;
}
Beispiel #13
0
TGoalVec ClearWayTo::getAllPossibleSubgoals()
{
	TGoalVec ret;
	for (auto h : cb->getHeroesInfo())
	{
		if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
			h->visitablePos() == tile) //we are already on that tile! what does it mean?
			continue;

		SectorMap sm(h);

		int3 tileToHit = sm.firstTileToGet(hero ? hero : h, tile);
		//if our hero is trapped, make sure we request clearing the way from OUR perspective
		if (!tileToHit.valid())
			continue;

		if (isBlockedBorderGate(tileToHit))
		{	//FIXME: this way we'll not visit gate and activate quest :?
			ret.push_back  (sptr (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID)));
		}

		auto topObj = cb->getTopObj(tileToHit);
		if(topObj)
		{
			if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
				if (topObj != hero.get(true)) //the hero we wnat to free
					logAi->errorStream() << boost::format("%s stands in the way of %s") % topObj->getHoverText()  % h->getHoverText();
			if (topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
			{
				if (shouldVisit(h, topObj))
				{
					//do NOT use VISIT_TILE, as tile with quets guard can't be visited
					ret.push_back (sptr (Goals::GetObj(topObj->id.getNum()).sethero(h)));
				}
				else
				{
					//TODO: we should be able to return apriopriate quest here (VCAI::striveToQuest)
					logAi->debugStream() << "Quest guard blocks the way to " + tile();
				}
			}
		}
		else
			ret.push_back (sptr (Goals::VisitTile(tileToHit).sethero(h)));
	}
	if (ai->canRecruitAnyHero())
		ret.push_back (sptr (Goals::RecruitHero()));

	if (ret.empty())
	{
		logAi->warnStream() << "There is no known way to clear the way to tile " + tile();
		throw goalFulfilledException (sptr(*this)); //make sure asigned hero gets unlocked
	}

	return ret;
}
Beispiel #14
0
TGoalVec VisitTile::getAllPossibleSubgoals()
{
	assert(cb->isInTheMap(tile));

	TGoalVec ret;
	if (!cb->isVisible(tile))
		ret.push_back (sptr(Goals::Explore())); //what sense does it make?
	else
	{
		std::vector<const CGHeroInstance *> heroes;
		if (hero)
			heroes.push_back(hero.h); //use assigned hero if any
		else
			heroes = cb->getHeroesInfo(); //use most convenient hero

		for (auto h : heroes)
		{
			if (ai->isAccessibleForHero(tile, h))
				ret.push_back (sptr(Goals::VisitTile(tile).sethero(h)));
		}
		if (ai->canRecruitAnyHero())
			ret.push_back (sptr(Goals::RecruitHero()));
	}
	if(ret.empty())
	{
		auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
		if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
		{
			if (hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
				ret.push_back(sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
			else
				throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier
		}
		else
			ret.push_back (sptr(Goals::ClearWayTo(tile)));
	}

	//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
	return ret;
}
Beispiel #15
0
TGoalVec GatherArmy::getAllPossibleSubgoals()
{
	//get all possible towns, heroes and dwellings we may use
	TGoalVec ret;
	
	//TODO: include evaluation of monsters gather in calculation
	for (auto t : cb->getTownsInfo())
	{
		auto pos = t->visitablePos();
		if (ai->isAccessibleForHero(pos, hero))
		{
			if(!t->visitingHero && howManyReinforcementsCanGet(hero,t))
			{
				if (!vstd::contains (ai->townVisitsThisWeek[hero], t))
					ret.push_back (sptr (Goals::VisitTile(pos).sethero(hero)));
			}
			auto bid = ai->canBuildAnyStructure(t, std::vector<BuildingID>
							(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
			if (bid != BuildingID::NONE)
				ret.push_back (sptr(BuildThis(bid, t)));
		}
	}

	auto otherHeroes = cb->getHeroesInfo();
	auto heroDummy = hero;
	erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
	{
		return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->visitablePos(), h, true)
			|| !ai->canGetArmy(heroDummy.h, h) || ai->getGoal(h)->goalType == Goals::GATHER_ARMY);
	});
	for (auto h : otherHeroes)
	{
		ret.push_back (sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero)));
				//go to the other hero if we are faster
		ret.push_back (sptr (Goals::VisitHero(hero->id.getNum()).setisAbstract(true).sethero(h)));
				//let the other hero come to us
	}

	std::vector <const CGObjectInstance *> objs;
	for (auto obj : ai->visitableObjs)
	{
		if(obj->ID == Obj::CREATURE_GENERATOR1)
		{
			auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);

			//Use flagged dwellings only when there are available creatures that we can afford
			if(relationToOwner == PlayerRelations::SAME_PLAYER)
			{
				auto dwelling = dynamic_cast<const CGDwelling*>(obj);
				for(auto & creLevel : dwelling->creatures)
				{
					if(creLevel.first)
					{
						for(auto & creatureID : creLevel.second)
						{
							auto creature = VLC->creh->creatures[creatureID];
							if (ai->freeResources().canAfford(creature->cost))
								objs.push_back(obj);
						}
					}
				}
			}
		}
	}
	for(auto h : cb->getHeroesInfo())
	{
		for (auto obj : objs)
		{ //find safe dwelling
			auto pos = obj->visitablePos();
			if (ai->isGoodForVisit(obj, h))
				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)
			ret.push_back (sptr(Goals::Explore()));
		else //workaround to break loop - seemingly there are no ways to explore left
			throw goalFulfilledException (sptr(Goals::GatherArmy(0).sethero(hero)));
	}

	return ret;
}
Beispiel #16
0
TGoalVec GatherArmy::getAllPossibleSubgoals()
{
	//get all possible towns, heroes and dwellings we may use
	TGoalVec ret;

	if(!hero.validAndSet())
	{
		return ret;
	}

	//TODO: include evaluation of monsters gather in calculation
	for(auto t : cb->getTownsInfo())
	{
		auto waysToVisit = ai->ah->howToVisitObj(hero, t);

		if(waysToVisit.size())
		{
			//grab army from town
			if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t))
			{
				if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
					vstd::concatenate(ret, waysToVisit);
			}

			//buy army in town
			if (!t->visitingHero || t->visitingHero == hero.get(true))
			{
				std::vector<int> values = {
					value,
					(int)howManyReinforcementsCanBuy(t->getUpperArmy(), t),
					(int)howManyReinforcementsCanBuy(hero.get(), t) };

				int val = *std::min_element(values.begin(), values.end());

				if (val)
				{
					auto goal = sptr(BuyArmy(t, val).sethero(hero));

					if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
						ret.push_back(goal);
					else
						logAi->debug("Can not buy army, because of ai->ah->containsObjective");
				}
			}
			//build dwelling
			//TODO: plan building over multiple turns?
			//auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));

			//Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
			/*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
			if (bid.is_initialized())
			{
				auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
				if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
					ret.push_back(goal);
				else
					logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
			}*/
		}
	}

	auto otherHeroes = cb->getHeroesInfo();
	auto heroDummy = hero;
	vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
	{
		if(h == heroDummy.h)
			return true;
		else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
			return true;
		else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue
			return true;
		else if(ai->getGoal(h)->goalType == GATHER_ARMY)
			return true;
		else
		   return false;
	});

	for(auto h : otherHeroes)
	{
		// Go to the other hero if we are faster
		if(!vstd::contains(ai->visitedHeroes[hero], h))
		{
			vstd::concatenate(ret, ai->ah->howToVisitObj(hero, h));
		}

		// Go to the other hero if we are faster
		if(!vstd::contains(ai->visitedHeroes[h], hero))
		{
			vstd::concatenate(ret, ai->ah->howToVisitObj(h, hero.get()));
		}
	}

	std::vector<const CGObjectInstance *> objs;
	for(auto obj : ai->visitableObjs)
	{
		if(obj->ID == Obj::CREATURE_GENERATOR1)
		{
			auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);

			//Use flagged dwellings only when there are available creatures that we can afford
			if(relationToOwner == PlayerRelations::SAME_PLAYER)
			{
				auto dwelling = dynamic_cast<const CGDwelling *>(obj);

				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero.get(), dwelling));

				if(val)
				{
					for(auto & creLevel : dwelling->creatures)
					{
						if(creLevel.first)
						{
							for(auto & creatureID : creLevel.second)
							{
								auto creature = VLC->creh->creatures[creatureID];
								if(ai->ah->freeResources().canAfford(creature->cost))
									objs.push_back(obj); //TODO: reserve resources?
							}
						}
					}
				}
			}
		}
	}

	for(auto h : cb->getHeroesInfo())
	{
		for(auto obj : objs)
		{
			//find safe dwelling
			if(ai->isGoodForVisit(obj, h))
			{
				vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj));
			}
		}
	}

	if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
	{
		if(auto t = ai->findTownWithTavern())
		{
			for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes
			{
				if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy
				{
					ret.push_back(sptr(RecruitHero()));
					break;
				}
			}
		}
	}

	if(ret.empty())
	{
		const bool allowGatherArmy = false;

		if(hero == ai->primaryHero())
			ret.push_back(sptr(Explore(allowGatherArmy)));
		else
			throw cannotFulfillGoalException("No ways to gather army");
	}

	return ret;
}
Beispiel #17
0
TGoalVec Explore::getAllPossibleSubgoals()
{
	TGoalVec ret;
	std::vector<const CGHeroInstance *> heroes;

	if (hero)
		heroes.push_back(hero.h);
	else
	{
		//heroes = ai->getUnblockedHeroes();
		heroes = cb->getHeroesInfo();
		erase_if (heroes, [](const HeroPtr h)
		{
			if (ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer
				return true;

			return !h->movement; //saves time, immobile heroes are useless anyway
		});
	}

	//try to use buildings that uncover map
	std::vector<const CGObjectInstance *> objs;
	for (auto obj : ai->visitableObjs)
	{
		if (!vstd::contains(ai->alreadyVisited, obj))
		{
			switch (obj->ID.num)
			{
				case Obj::REDWOOD_OBSERVATORY:
				case Obj::PILLAR_OF_FIRE:
				case Obj::CARTOGRAPHER:
				case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates
					objs.push_back (obj);
			}
		}
	}
	for (auto h : heroes)
	{
		SectorMap sm(h);

		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 (ai->canReachTile(h, t))
				ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
		}

		int3 t = whereToExplore(h);
		if (t.valid())
		{
			ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
		}
		else if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost
		{
			t = ai->explorationDesperate(h);
			if (t.valid()) //don't waste time if we are completely blocked
				ret.push_back (sptr(Goals::ClearWayTo(t, h).setisAbstract(true)));
		}
	}
	//we either don't have hero yet or none of heroes can explore
	if ((!hero || ret.empty()) && ai->canRecruitAnyHero())
		ret.push_back (sptr(Goals::RecruitHero()));

	if (ret.empty())
	{
		throw goalFulfilledException (sptr(Goals::Explore().sethero(hero)));
	}
	//throw cannotFulfillGoalException("Cannot explore - no possible ways found!");

	return ret;
}
Beispiel #18
0
TGoalVec ClearWayTo::getAllPossibleSubgoals()
{
	TGoalVec ret;

	std::vector<const CGHeroInstance *> heroes;

	if (hero)
		heroes.push_back(hero.h);
	else
	{
		heroes = cb->getHeroesInfo();
	}

	for (auto h : heroes)
	{
		//TODO: handle clearing way to allied heroes that are blocked
		//if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
		//	h->visitablePos() == tile) //we are already on that tile! what does it mean?
		//	continue;

		//if our hero is trapped, make sure we request clearing the way from OUR perspective

		SectorMap sm(h);

		int3 tileToHit = sm.firstTileToGet(h, tile);
		if (!tileToHit.valid())
			continue;

		if (isBlockedBorderGate(tileToHit))
		{	//FIXME: this way we'll not visit gate and activate quest :?
			ret.push_back  (sptr (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID)));
		}

		auto topObj = cb->getTopObj(tileToHit);
		if (topObj)
		{
			if (vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[h], topObj))
			{
				throw goalFulfilledException (sptr(Goals::ClearWayTo(tile, h)));
				continue; //do not capure object reserved by other hero
			}

			if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
				if (topObj != hero.get(true)) //the hero we want to free
					logAi->errorStream() << boost::format("%s stands in the way of %s") % topObj->getHoverText()  % h->getHoverText();
			if (topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
			{
				if (shouldVisit(h, topObj))
				{
					//do NOT use VISIT_TILE, as tile with quets guard can't be visited
					ret.push_back (sptr (Goals::GetObj(topObj->id.getNum()).sethero(h)));
					continue; //do not try to visit tile or gather army
				}
				else
				{
					//TODO: we should be able to return apriopriate quest here (VCAI::striveToQuest)
					logAi->debugStream() << "Quest guard blocks the way to " + tile();
				}
			}
		}

		if (isSafeToVisit(h, tileToHit)) //this makes sense only if tile is guarded, but there i no quest object
		{
			ret.push_back (sptr (Goals::VisitTile(tileToHit).sethero(h)));
		}
		else
		{
			ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(tileToHit, h)*SAFE_ATTACK_CONSTANT).
				sethero(h).setisAbstract(true)));
		}
	}
	if (ai->canRecruitAnyHero())
		ret.push_back (sptr (Goals::RecruitHero()));

	if (ret.empty())
	{
		logAi->warnStream() << "There is no known way to clear the way to tile " + tile();
		throw goalFulfilledException (sptr(Goals::ClearWayTo(tile))); //make sure asigned hero gets unlocked
	}

	return ret;
}
Beispiel #19
0
TGoalVec Conquer::getAllPossibleSubgoals()
{
	TGoalVec ret;

	auto conquerable = [](const CGObjectInstance * obj) -> bool
	{
		if (cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
		{
			switch (obj->ID.num)
			{
				case Obj::TOWN:
				case Obj::HERO:
				case Obj::CREATURE_GENERATOR1:
				case Obj::MINE: //TODO: check ai->knownSubterraneanGates
					return true;
			}
		}
		return false;
	};

	std::vector<const CGObjectInstance *> objs;
	for (auto obj : ai->visitableObjs)
	{
		if (conquerable(obj))
			objs.push_back (obj);
	}

	for (auto h : cb->getHeroesInfo())
	{
		auto sm = ai->getCachedSectorMap(h);
		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects

		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
		{
			if (conquerable(obj))
				ourObjs.push_back(obj);
		}
		for (auto obj : ourObjs)
		{
			int3 dest = obj->visitablePos();
			auto t = sm->firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded
			if (t.valid()) //we know any path at all
			{
				if (ai->isTileNotReserved(h, t)) //no other hero wants to conquer that tile
				{
					if (isSafeToVisit(h, dest))
					{
						if (dest != t) //there is something blocking our way
							ret.push_back(sptr(Goals::ClearWayTo(dest, h).setisAbstract(true)));
						else
						{
							if (obj->ID.num == Obj::HERO) //enemy hero may move to other position
								ret.push_back(sptr(Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true)));
							else //just visit that tile
								ret.push_back(sptr(Goals::VisitTile(dest).sethero(h).setisAbstract(true)));
						}
					}
					else //we need to get army in order to conquer that place
						ret.push_back(sptr(Goals::GatherArmy(evaluateDanger(dest, h) * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true)));
				}
			}
		}
	}
	if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
		ret.push_back (sptr(Goals::RecruitHero()));

	if (ret.empty())
		ret.push_back (sptr(Goals::Explore())); //we need to find an enemy
	return ret;
}
Beispiel #20
0
TGoalVec Explore::getAllPossibleSubgoals()
{
	TGoalVec ret;
	std::vector<const CGHeroInstance *> heroes;

	if (hero)
		heroes.push_back(hero.h);
	else
	{
		//heroes = ai->getUnblockedHeroes();
		heroes = cb->getHeroesInfo();
		vstd::erase_if(heroes, [](const HeroPtr h)
		{
			if (ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer
				return true;

			if (!ai->isAbleToExplore(h))
				return true;

			return !h->movement; //saves time, immobile heroes are useless anyway
		});
	}

	//try to use buildings that uncover map
	std::vector<const CGObjectInstance *> objs;
	for (auto obj : ai->visitableObjs)
	{
		if (!vstd::contains(ai->alreadyVisited, obj))
		{
			switch (obj->ID.num)
			{
			case Obj::REDWOOD_OBSERVATORY:
			case Obj::PILLAR_OF_FIRE:
			case Obj::CARTOGRAPHER:
				objs.push_back (obj);
				break;
			case Obj::MONOLITH_ONE_WAY_ENTRANCE:
			case Obj::MONOLITH_TWO_WAY:
			case Obj::SUBTERRANEAN_GATE:
				auto tObj = dynamic_cast<const CGTeleport *>(obj);
				assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end());
				if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability)
					objs.push_back (obj);
				break;
			}
		}
		else
		{
			switch (obj->ID.num)
			{
			case Obj::MONOLITH_TWO_WAY:
			case Obj::SUBTERRANEAN_GATE:
				auto tObj = dynamic_cast<const CGTeleport *>(obj);
				if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability)
					break;
				for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits)
				{
					if(!cb->getObj(exit))
					{ // Always attempt to visit two-way teleports if one of channel exits is not visible
						objs.push_back(obj);
						break;
					}
				}
				break;
			}
		}
	}

	for (auto h : heroes)
	{
		auto sm = ai->getCachedSectorMap(h);

		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 (ai->isTileNotReserved(h, t))
				ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
		}

		int3 t = whereToExplore(h);
		if (t.valid())
		{
			ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
		}
		else
		{
			ai->markHeroUnableToExplore (h); //there is no freely accessible tile, do not poll this hero anymore
			//possible issues when gathering army to break

			if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost
			{
				t = ai->explorationDesperate(h);
				if (t.valid()) //don't waste time if we are completely blocked
					ret.push_back (sptr(Goals::ClearWayTo(t, h).setisAbstract(true)));
			}
		}
	}
	//we either don't have hero yet or none of heroes can explore
	if ((!hero || ret.empty()) && ai->canRecruitAnyHero())
		ret.push_back (sptr(Goals::RecruitHero()));

	if (ret.empty())
	{
		throw goalFulfilledException (sptr(Goals::Explore().sethero(hero)));
	}
	//throw cannotFulfillGoalException("Cannot explore - no possible ways found!");

	return ret;
}