Example #1
0
File: Goals.cpp Project: szpak/vcmi
bool VisitHero::fulfillsMe (TSubgoal goal)
{
	if (goal->goalType == Goals::VISIT_TILE && cb->getObj(ObjectInstanceID(objid))->visitablePos() == goal->tile)
		return true;
	else
		return false;
}
Example #2
0
const CGHeroInstance * HeroPtr::get(bool doWeExpectNull /*= false*/) const
{
	//TODO? check if these all assertions every time we get info about hero affect efficiency
	//
	//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
	assert(doWeExpectNull || h);

	if(h)
	{
		auto obj = cb->getObj(hid);
		const bool owned = obj && obj->tempOwner == ai->playerID;

		if(doWeExpectNull && !owned)
		{
			return nullptr;
		}
		else
		{
			assert(obj);
			assert(owned);
		}
	}

	return h;
}
Example #3
0
File: Goals.cpp Project: szpak/vcmi
bool GetObj::fulfillsMe (TSubgoal goal)
{
	if (goal->goalType == Goals::VISIT_TILE)
	{
		auto obj = cb->getObj(ObjectInstanceID(objid));
		if (obj && obj->visitablePos() == goal->tile) //object could be removed
			return true;
	}

	return false;
}
Example #4
0
bool VisitHero::fulfillsMe (TSubgoal goal)
{
	if (goal->goalType != Goals::VISIT_TILE)
	{
		return false;
	}
	auto obj = cb->getObj(ObjectInstanceID(objid));
	if (!obj)
	{
		logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile, objid);
		return false;
	}
	return obj->visitablePos() == goal->tile;
}
Example #5
0
void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
{
	Sector & s = infoOnSectors[num];
	s.id = num;
	s.water = getTile(pos)->isWater();

	std::queue<int3> toVisit;
	toVisit.push(pos);
	while (!toVisit.empty())
	{
		int3 curPos = toVisit.front();
		toVisit.pop();
		TSectorID & sec = retrieveTile(curPos);
		if (sec == NOT_CHECKED)
		{
			const TerrainTile * t = getTile(curPos);
			if (!markIfBlocked(sec, curPos, t))
			{
				if (t->isWater() == s.water) //sector is only-water or only-land
				{
					sec = num;
					s.tiles.push_back(curPos);
					foreach_neighbour(cbp, curPos, [&](CCallback * cbp, crint3 neighPos)
					{
						if (retrieveTile(neighPos) == NOT_CHECKED)
						{
							toVisit.push(neighPos);
							//parent[neighPos] = curPos;
						}
						const TerrainTile * nt = getTile(neighPos);
						if (nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water))
						{
							s.embarkmentPoints.push_back(neighPos);
						}
					});

					if (t->visitable)
					{
						auto obj = t->visitableObjects.front();
						if (cb->getObj(obj->id, false)) // FIXME: we have to filter invisible objcts like events, but probably TerrainTile shouldn't be used in SectorMap at all
							s.visitableObjs.push_back(obj);
					}
				}
			}
		}
	}

	vstd::removeDuplicates(s.embarkmentPoints);
}
Example #6
0
File: Goals.cpp Project: szpak/vcmi
TSubgoal GetObj::whatToDoToAchieve()
{
	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
	if(!obj)
		return sptr (Goals::Explore());
	int3 pos = obj->visitablePos();
	if (hero)
	{
		if (ai->isAccessibleForHero(pos, hero))
			return sptr (Goals::VisitTile(pos).sethero(hero));
	}
	else
	{
		if (isReachable(obj))
			return sptr (Goals::VisitTile(pos).sethero(hero)); //we must visit object with same hero, if any
	}
	return sptr (Goals::ClearWayTo(pos).sethero(hero));
}
Example #7
0
File: Goals.cpp Project: szpak/vcmi
TSubgoal VisitHero::whatToDoToAchieve()
{
	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
	if(!obj)
		return sptr (Goals::Explore());
	int3 pos = obj->visitablePos();

	if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
	{
		if (hero->pos == pos)
			logAi->errorStream() << "Hero " << hero.name << " tries to visit himself.";
		else
		{
			//can't use VISIT_TILE here as tile appears blocked by target hero
			//FIXME: elementar goal should not be abstract
			return sptr (Goals::VisitHero(objid).sethero(hero).settile(pos).setisElementar(true));
		}
	}
	return sptr (Goals::Invalid());
}
Example #8
0
TSubgoal GetObj::whatToDoToAchieve()
{
	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
	if(!obj)
		return sptr (Goals::Explore());
	if (obj->tempOwner == ai->playerID) //we can't capture our own object -> move to Win codition
		throw cannotFulfillGoalException("Cannot capture my own object " + obj->getObjectName());

	int3 pos = obj->visitablePos();
	if (hero)
	{
		if (ai->isAccessibleForHero(pos, hero))
			return sptr (Goals::VisitTile(pos).sethero(hero));
	}
	else
	{
		for (auto h : cb->getHeroesInfo())
		{
			if (ai->isAccessibleForHero(pos, h))
				return sptr(Goals::VisitTile(pos).sethero(h)); //we must visit object with same hero, if any
		}
	}
	return sptr (Goals::ClearWayTo(pos).sethero(hero));
}
Example #9
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;
}
Example #10
0
TSubgoal Win::whatToDoToAchieve()
{
	auto toBool = [=](const EventCondition &)
	{
		// TODO: proper implementation
		// Right now even already fulfilled goals will be included into generated list
		// Proper check should test if event condition is already fulfilled
		// Easiest way to do this is to call CGameState::checkForVictory but this function should not be
		// used on client side or in AI code
		return false;
	};

	std::vector<EventCondition> goals;

	for (const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
	{
		//TODO: try to eliminate human player(s) using loss conditions that have isHuman element

		if (event.effect.type == EventEffect::VICTORY)
		{
			boost::range::copy(event.trigger.getFulfillmentCandidates(toBool), std::back_inserter(goals));
		}
	}

	//TODO: instead of returning first encountered goal AI should generate list of possible subgoals
	for (const EventCondition & goal : goals)
	{
		switch(goal.condition)
		{
		case EventCondition::HAVE_ARTIFACT:
			return sptr (Goals::GetArtOfType(goal.objectType));
		case EventCondition::DESTROY:
			{
				if (goal.object)
				{
					auto obj = cb->getObj (goal.object->id);
					if (obj)
						if (obj->getOwner() == ai->playerID) //we can't capture our own object
							return sptr(Goals::Conquer());


					return sptr (Goals::GetObj(goal.object->id.getNum()));
				}
				else
				{
					// TODO: destroy all objects of type goal.objectType
					// This situation represents "kill all creatures" condition from H3
					break;
				}
			}
		case EventCondition::HAVE_BUILDING:
			{
			// TODO build other buildings apart from Grail
			// goal.objectType = buidingID to build
			// goal.object = optional, town in which building should be built
			// Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)

				if (goal.objectType == BuildingID::GRAIL)
				{
					if(auto h = ai->getHeroWithGrail())
					{
						//hero is in a town that can host Grail
						if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
						{
							const CGTownInstance *t = h->visitedTown;
							return sptr (Goals::BuildThis(BuildingID::GRAIL, t));
						}
						else
						{
							auto towns = cb->getTownsInfo();
							towns.erase(boost::remove_if(towns,
												[](const CGTownInstance *t) -> bool
												{
													return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
												}),
										towns.end());
							boost::sort(towns, CDistanceSorter(h.get()));
							if(towns.size())
							{
								return sptr (Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
							}
						}
					}
					double ratio = 0;
					// maybe make this check a bit more complex? For example:
					// 0.75 -> dig randomly within 3 tiles radius
					// 0.85 -> radius now 2 tiles
					// 0.95 -> 1 tile radius, position is fully known
					// AFAIK H3 AI does something like this
					int3 grailPos = cb->getGrailPos(&ratio);
					if(ratio > 0.99)
					{
						return sptr (Goals::DigAtTile(grailPos));
					} //TODO: use FIND_OBJ
					else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
						return sptr (Goals::GetObj(obj->id.getNum()));
					else
						return sptr (Goals::Explore());
				}
				break;
			}
		case EventCondition::CONTROL:
			{
				if (goal.object)
				{
					return sptr (Goals::GetObj(goal.object->id.getNum()));
				}
				else
				{
					//TODO: control all objects of type "goal.objectType"
					// Represents H3 condition "Flag all mines"
					break;
				}
			}

		case EventCondition::HAVE_RESOURCES:
			//TODO mines? piles? marketplace?
			//save?
			return sptr (Goals::CollectRes(static_cast<Res::ERes>(goal.objectType), goal.value));
		case EventCondition::HAVE_CREATURES:
			return sptr (Goals::GatherTroops(goal.objectType, goal.value));
		case EventCondition::TRANSPORT:
			{
				//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
				// Represents "transport artifact" condition:
				// goal.objectType = type of artifact
				// goal.object = destination-town where artifact should be transported
				break;
			}
		case EventCondition::STANDARD_WIN:
			return sptr (Goals::Conquer());

		// Conditions that likely don't need any implementation
		case EventCondition::DAYS_PASSED:
			break; // goal.value = number of days for condition to trigger
		case EventCondition::DAYS_WITHOUT_TOWN:
			break; // goal.value = number of days to trigger this
		case EventCondition::IS_HUMAN:
			break; // Should be only used in calculation of candidates (see toBool lambda)
		case EventCondition::CONST_VALUE:
			break;
		default:
			assert(0);
		}
	}
	return sptr (Goals::Invalid());
}
Example #11
0
ObjectIdRef::operator const CGObjectInstance*() const
{
	return cb->getObj(id, false);
}