| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 | /* * CMapGenOptions.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */#include "StdInc.h"#include "CMapGenOptions.h"#include "../mapping/CMapHeader.h"#include "CRmgTemplateStorage.h"#include "CRmgTemplate.h"#include "CRandomGenerator.h"#include "../VCMI_Lib.h"#include "../CTownHandler.h"VCMI_LIB_NAMESPACE_BEGINCMapGenOptions::CMapGenOptions()	: width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(true),	playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(RANDOM_SIZE), compOnlyTeamCount(RANDOM_SIZE),	waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr){	resetPlayersMap();	setRoadEnabled(RoadId(Road::DIRT_ROAD), true);	setRoadEnabled(RoadId(Road::GRAVEL_ROAD), true);	setRoadEnabled(RoadId(Road::COBBLESTONE_ROAD), true);}si32 CMapGenOptions::getWidth() const{	return width;}void CMapGenOptions::setWidth(si32 value){	assert(value >= 1);	width = value;}si32 CMapGenOptions::getHeight() const{	return height;}void CMapGenOptions::setHeight(si32 value){	assert(value >= 1);	height = value;}bool CMapGenOptions::getHasTwoLevels() const{	return hasTwoLevels;}void CMapGenOptions::setHasTwoLevels(bool value){	hasTwoLevels = value;}si8 CMapGenOptions::getPlayerCount() const{	return playerCount;}void CMapGenOptions::setPlayerCount(si8 value){	assert((value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I) || value == RANDOM_SIZE);	playerCount = value;	auto possibleCompPlayersCount = PlayerColor::PLAYER_LIMIT_I - value;	if (compOnlyPlayerCount > possibleCompPlayersCount)		setCompOnlyPlayerCount(possibleCompPlayersCount);	resetPlayersMap();}si8 CMapGenOptions::getTeamCount() const{	return teamCount;}void CMapGenOptions::setTeamCount(si8 value){	assert(getPlayerCount() == RANDOM_SIZE || (value >= 0 && value < getPlayerCount()) || value == RANDOM_SIZE);	teamCount = value;}si8 CMapGenOptions::getCompOnlyPlayerCount() const{	return compOnlyPlayerCount;}void CMapGenOptions::setCompOnlyPlayerCount(si8 value){	assert(value == RANDOM_SIZE || (getPlayerCount() == RANDOM_SIZE || (value >= 0 && value <= PlayerColor::PLAYER_LIMIT_I - getPlayerCount())));	compOnlyPlayerCount = value;	resetPlayersMap();}si8 CMapGenOptions::getCompOnlyTeamCount() const{	return compOnlyTeamCount;}void CMapGenOptions::setCompOnlyTeamCount(si8 value){	assert(value == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayerCount - 1, 0)));	compOnlyTeamCount = value;}EWaterContent::EWaterContent CMapGenOptions::getWaterContent() const{	return waterContent;}void CMapGenOptions::setWaterContent(EWaterContent::EWaterContent value){	waterContent = value;}EMonsterStrength::EMonsterStrength CMapGenOptions::getMonsterStrength() const{	return monsterStrength;}void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value){ 	monsterStrength = value;}void CMapGenOptions::resetPlayersMap(){	std::map<PlayerColor, FactionID> rememberTownTypes;	std::map<PlayerColor, TeamID> rememberTeam;	for(const auto & p : players)	{		auto town = p.second.getStartingTown();		if (town != RANDOM_SIZE)			rememberTownTypes[p.first] = FactionID(town);		rememberTeam[p.first] = p.second.getTeam();	}	players.clear();	int realPlayersCnt = playerCount;	int realCompOnlyPlayersCnt = (compOnlyPlayerCount == RANDOM_SIZE) ? (PlayerColor::PLAYER_LIMIT_I - realPlayersCnt) : compOnlyPlayerCount;	int totalPlayersLimit = realPlayersCnt + realCompOnlyPlayersCnt;	if (getPlayerCount() == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE)		totalPlayersLimit = static_cast<int>(PlayerColor::PLAYER_LIMIT_I);	//FIXME: what happens with human players here?	for(int color = 0; color < totalPlayersLimit; ++color)	{		CPlayerSettings player;		auto pc = PlayerColor(color);		player.setColor(pc);		auto playerType = EPlayerType::AI;		if (getPlayerCount() != RANDOM_SIZE && color < realPlayersCnt)		{			playerType = EPlayerType::HUMAN;		}		else if((getPlayerCount() != RANDOM_SIZE && color >= realPlayersCnt)		   || (compOnlyPlayerCount != RANDOM_SIZE && color >= (PlayerColor::PLAYER_LIMIT_I-compOnlyPlayerCount)))		{			playerType = EPlayerType::COMP_ONLY;		}		player.setPlayerType(playerType);		player.setTeam(rememberTeam[pc]);		players[pc] = player;		if (vstd::contains(rememberTownTypes, pc))			players[pc].setStartingTown(rememberTownTypes[pc]);	}}const std::map<PlayerColor, CMapGenOptions::CPlayerSettings> & CMapGenOptions::getPlayersSettings() const{	return players;}void CMapGenOptions::setStartingTownForPlayer(const PlayerColor & color, si32 town){	auto it = players.find(color);	if(it == players.end()) assert(0);	it->second.setStartingTown(town);}void CMapGenOptions::setPlayerTypeForStandardPlayer(const PlayerColor & color, EPlayerType playerType){	assert(playerType != EPlayerType::COMP_ONLY);	auto it = players.find(color);	if(it == players.end()) assert(0);	it->second.setPlayerType(playerType);}const CRmgTemplate * CMapGenOptions::getMapTemplate() const{	return mapTemplate;}void CMapGenOptions::setMapTemplate(const CRmgTemplate * value){	mapTemplate = value;	//validate & adapt options according to template	if(mapTemplate)	{		if(!mapTemplate->matchesSize(int3(getWidth(), getHeight(), 1 + getHasTwoLevels())))		{			auto sizes = mapTemplate->getMapSizes();			setWidth(sizes.first.x);			setHeight(sizes.first.y);			setHasTwoLevels(sizes.first.z - 1);		}				if(!mapTemplate->getPlayers().isInRange(getPlayerCount()))			setPlayerCount(RANDOM_SIZE);		if(!mapTemplate->getCpuPlayers().isInRange(getCompOnlyPlayerCount()))			setCompOnlyPlayerCount(RANDOM_SIZE);		if(!mapTemplate->getWaterContentAllowed().count(getWaterContent()))			setWaterContent(EWaterContent::RANDOM);	}}void CMapGenOptions::setMapTemplate(const std::string & name){	if(!name.empty())		setMapTemplate(VLC->tplh->getTemplate(name));}void CMapGenOptions::setRoadEnabled(const RoadId & roadType, bool enable){	if (enable)	{		enabledRoads.insert(roadType);	}	else	{		enabledRoads.erase(roadType);	}	}bool CMapGenOptions::isRoadEnabled(const RoadId & roadType) const{	return enabledRoads.count(roadType);}bool CMapGenOptions::isRoadEnabled() const{	return !enabledRoads.empty();}void CMapGenOptions::setPlayerTeam(const PlayerColor & color, const TeamID & team){	auto it = players.find(color);	if(it == players.end()) assert(0);	it->second.setTeam(team);}void CMapGenOptions::finalize(CRandomGenerator & rand){	logGlobal->info("RMG map: %dx%d, %s underground", getWidth(), getHeight(), getHasTwoLevels() ? "WITH" : "NO");	logGlobal->info("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d",		static_cast<int>(getPlayerCount()), static_cast<int>(getTeamCount()), static_cast<int>(getCompOnlyPlayerCount()),		static_cast<int>(getCompOnlyTeamCount()), static_cast<int>(getWaterContent()), static_cast<int>(getMonsterStrength()));	if(!mapTemplate)	{		mapTemplate = getPossibleTemplate(rand);	}	assert(mapTemplate);		logGlobal->info("RMG template name: %s", mapTemplate->getName());	if (getPlayerCount() == RANDOM_SIZE)	{		auto possiblePlayers = mapTemplate->getPlayers().getNumbers();		//ignore all non-randomized players, make sure these players will not be missing after roll		possiblePlayers.erase(possiblePlayers.begin(), possiblePlayers.lower_bound(countHumanPlayers() + countCompOnlyPlayers()));		assert(!possiblePlayers.empty());		setPlayerCount (*RandomGeneratorUtil::nextItem(possiblePlayers, rand));		updatePlayers();	}	if(teamCount == RANDOM_SIZE)	{		teamCount = rand.nextInt(getPlayerCount() - 1);		if (teamCount == 1)			teamCount = 0;	}	if(compOnlyPlayerCount == RANDOM_SIZE)	{		auto possiblePlayers = mapTemplate->getCpuPlayers().getNumbers();		compOnlyPlayerCount = *RandomGeneratorUtil::nextItem(possiblePlayers, rand);		updateCompOnlyPlayers();	}	if(compOnlyTeamCount == RANDOM_SIZE)	{		compOnlyTeamCount = rand.nextInt(std::max(compOnlyPlayerCount - 1, 0));	}	if(waterContent == EWaterContent::RANDOM)	{		auto allowedContent = mapTemplate->getWaterContentAllowed();		if(!allowedContent.empty())		{			waterContent = *RandomGeneratorUtil::nextItem(mapTemplate->getWaterContentAllowed(), rand);		}		else		{			waterContent = EWaterContent::NONE;		}	}	if(monsterStrength == EMonsterStrength::RANDOM)	{		monsterStrength = static_cast<EMonsterStrength::EMonsterStrength>(rand.nextInt(EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG));	}	assert (vstd::iswithin(waterContent, EWaterContent::NONE, EWaterContent::ISLANDS));	assert (vstd::iswithin(monsterStrength, EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG));	//rectangular maps are the future of gaming	//setHeight(20);	//setWidth(50);	logGlobal->trace("Player config:");	int cpuOnlyPlayers = 0;	for(const auto & player : players)	{		std::string playerType;		switch (player.second.getPlayerType())		{		case EPlayerType::AI:			playerType = "AI";			break;		case EPlayerType::COMP_ONLY:			playerType = "computer only";			cpuOnlyPlayers++;			break;		case EPlayerType::HUMAN:			playerType = "human only";			break;			default:				assert(false);		}		logGlobal->trace("Player %d: %s", player.second.getColor(), playerType);	}	setCompOnlyPlayerCount(cpuOnlyPlayers); //human players are set automaticlaly (?)	logGlobal->info("Final player config: %d total, %d cpu-only", players.size(), static_cast<int>(getCompOnlyPlayerCount()));}void CMapGenOptions::updatePlayers(){	// Remove AI players only from the end of the players map if necessary	for(auto itrev = players.end(); itrev != players.begin();)	{		auto it = itrev;		--it;		if (players.size() == getPlayerCount()) break;		if(it->second.getPlayerType() == EPlayerType::AI)		{			players.erase(it);		}		else		{			--itrev;		}	}}void CMapGenOptions::updateCompOnlyPlayers(){	// Remove comp only players only from the end of the players map if necessary	for(auto itrev = players.end(); itrev != players.begin();)	{		auto it = itrev;		--it;		if (players.size() <= getPlayerCount()) break;		if(it->second.getPlayerType() == EPlayerType::COMP_ONLY)		{			players.erase(it);		}		else		{			--itrev;		}	}	// Add some comp only players if necessary	int compOnlyPlayersToAdd = static_cast<int>(getPlayerCount() - players.size());	if (compOnlyPlayersToAdd < 0)	{		logGlobal->error("Incorrect number of players to add. Requested players %d, current players %d", playerCount, players.size());		assert (compOnlyPlayersToAdd < 0);	}	for(int i = 0; i < compOnlyPlayersToAdd; ++i)	{		CPlayerSettings pSettings;		pSettings.setPlayerType(EPlayerType::COMP_ONLY);		pSettings.setColor(getNextPlayerColor());		players[pSettings.getColor()] = pSettings;	}}int CMapGenOptions::countHumanPlayers() const{	return static_cast<int>(boost::count_if(players, [](const std::pair<PlayerColor, CPlayerSettings> & pair)	{		return pair.second.getPlayerType() == EPlayerType::HUMAN;	}));}int CMapGenOptions::countCompOnlyPlayers() const{	return static_cast<int>(boost::count_if(players, [](const std::pair<PlayerColor, CPlayerSettings> & pair)	{		return pair.second.getPlayerType() == EPlayerType::COMP_ONLY;	}));}PlayerColor CMapGenOptions::getNextPlayerColor() const{	for(PlayerColor i = PlayerColor(0); i < PlayerColor::PLAYER_LIMIT; i.advance(1))	{		if(!players.count(i))		{			return i;		}	}	logGlobal->error("Failed to get next player color");	assert(false);	return PlayerColor(0);}bool CMapGenOptions::checkOptions() const{	if(mapTemplate)	{		return true;	}	else	{		CRandomGenerator gen;		return getPossibleTemplate(gen) != nullptr;	}}std::vector<const CRmgTemplate *> CMapGenOptions::getPossibleTemplates() const{	int3 tplSize(width, height, (hasTwoLevels ? 2 : 1));	auto humanPlayers = countHumanPlayers();	auto templates = VLC->tplh->getTemplates();	vstd::erase_if(templates, [this, &tplSize, humanPlayers](const CRmgTemplate * tmpl)	{		if(!tmpl->matchesSize(tplSize))			return true;		if(!tmpl->isWaterContentAllowed(getWaterContent()))			return true;		if(getPlayerCount() != -1)		{			if (!tmpl->getPlayers().isInRange(getPlayerCount()))				return true;		}		else		{			// Human players shouldn't be banned when playing with random player count			if(humanPlayers > *boost::min_element(tmpl->getPlayers().getNumbers()))				return true;		}		if(compOnlyPlayerCount != -1)		{			if (!tmpl->getCpuPlayers().isInRange(compOnlyPlayerCount))				return true;		}		return false;	});	return templates;}const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand) const{	auto templates = getPossibleTemplates();	if(templates.empty())		return nullptr;		return *RandomGeneratorUtil::nextItem(templates, rand);}CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI), team(TeamID::NO_TEAM){}PlayerColor CMapGenOptions::CPlayerSettings::getColor() const{	return color;}void CMapGenOptions::CPlayerSettings::setColor(const PlayerColor & value){	assert(value >= PlayerColor(0) && value < PlayerColor::PLAYER_LIMIT);	color = value;}si32 CMapGenOptions::CPlayerSettings::getStartingTown() const{	return startingTown;}void CMapGenOptions::CPlayerSettings::setStartingTown(si32 value){	assert(value >= -1);	if(value >= 0)	{		assert(value < static_cast<int>(VLC->townh->size()));		assert((*VLC->townh)[value]->town != nullptr);	}	startingTown = value;}EPlayerType CMapGenOptions::CPlayerSettings::getPlayerType() const{	return playerType;}void CMapGenOptions::CPlayerSettings::setPlayerType(EPlayerType value){	playerType = value;}TeamID CMapGenOptions::CPlayerSettings::getTeam() const{	return team;}void CMapGenOptions::CPlayerSettings::setTeam(const TeamID & value){	team = value;}VCMI_LIB_NAMESPACE_END
 |