mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	- fixed crash on start of some campaigns
- allowed on map factions is now set instead of bit field
This commit is contained in:
		| @@ -1914,41 +1914,30 @@ void OptionsTab::nextCastle( int player, int dir ) | ||||
|  | ||||
| 	PlayerSettings &s = SEL->sInfo.playerInfos[player]; | ||||
| 	si32 &cur = s.castle; | ||||
| 	ui32 allowed = SEL->current->mapHeader->players[s.color].allowedFactions; | ||||
| 	auto & allowed = SEL->current->mapHeader->players[s.color].allowedFactions; | ||||
|  | ||||
| 	if (cur == -2) //no castle - no change | ||||
| 		return; | ||||
|  | ||||
| 	if (cur == -1) //random => first/last available | ||||
| 	{ | ||||
| 		int pom = (dir>0) ? (0) : (GameConstants::F_NUMBER-1); // last or first | ||||
| 		for (;pom >= 0  &&  pom < GameConstants::F_NUMBER;  pom+=dir) | ||||
| 		{ | ||||
| 			if((1 << pom) & allowed) | ||||
| 			{ | ||||
| 				cur=pom; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (dir > 0) | ||||
| 			cur = *allowed.begin(); //id of first town | ||||
| 		else | ||||
| 			cur = *allowed.rbegin(); //id of last town | ||||
|  | ||||
| 	} | ||||
| 	else // next/previous available | ||||
| 	{ | ||||
| 		for (;;) | ||||
| 		if ( (cur == *allowed.begin() && dir < 0 ) | ||||
| 		  || (cur == *allowed.rbegin() && dir > 0) ) | ||||
| 			cur = -1; | ||||
| 		else | ||||
| 		{ | ||||
| 			cur+=dir; | ||||
| 			if ((1 << cur) & allowed) | ||||
| 				break; | ||||
|  | ||||
| 			if (cur >= GameConstants::F_NUMBER  ||  cur<0) | ||||
| 			{ | ||||
| 				double p1 = log((double)allowed) / log(2.0)+0.000001; | ||||
| 				double check = p1 - ((int)p1); | ||||
| 				if (check < 0.001) | ||||
| 					cur = (int)p1; | ||||
| 				else | ||||
| 					cur = -1; | ||||
| 				break; | ||||
| 			} | ||||
| 			assert(dir >= -1 && dir <= 1); //othervice std::advance may go out of range | ||||
| 			auto iter = allowed.find(cur); | ||||
| 			std::advance(iter, dir); | ||||
| 			cur = *iter; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -448,7 +448,7 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg | ||||
| 	if (creature) | ||||
| 	{ | ||||
| 		std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT"; | ||||
| 		creatureImage = new CAnimImage(imgName, creature->idNumber + 2); | ||||
| 		creatureImage = new CAnimImage(imgName, creature->iconIndex); | ||||
| 	} | ||||
| 	else | ||||
| 		creatureImage = nullptr; | ||||
|   | ||||
| @@ -52,23 +52,18 @@ unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & name ) | ||||
| 		ret->scenarios.push_back(sc); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	int scenarioID = 0; | ||||
|  | ||||
| 	//first entry is campaign header. start loop from 1 | ||||
| 	for (int g=1; g<file.size() && g<howManyScenarios; ++g) | ||||
| 	for (int g=1; g<file.size() && scenarioID<howManyScenarios; ++g) | ||||
| 	{ | ||||
| 		while(!ret->scenarios[scenarioID].isNotVoid()) //skip void scenarios | ||||
| 		{ | ||||
| 			scenarioID++; | ||||
| 		} | ||||
| 		//set map piece appropriately | ||||
|  | ||||
|  | ||||
| 		ret->mapPieces[scenarioID].resize(file[g].size()); | ||||
| 		for(int i = 0; i < file[g].size(); i++) | ||||
| 			ret->mapPieces[scenarioID][i] = file[g][i]; | ||||
| 		 | ||||
| 		//set map piece appropriately, convert vector to string | ||||
| 		ret->mapPieces[scenarioID].assign(reinterpret_cast< const char* >(file[g].data()), file[g].size()); | ||||
| 		scenarioID++; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -931,12 +931,11 @@ void CGameState::init(StartInfo * si) | ||||
| 	{ | ||||
| 		if(it->second.castle==-1) | ||||
| 		{ | ||||
| 			int f; | ||||
| 			do | ||||
| 			{ | ||||
| 				f = ran()%GameConstants::F_NUMBER; | ||||
| 			}while(!(map->players[it->first].allowedFactions  &  1<<f)); | ||||
| 			it->second.castle = f; | ||||
| 			int randomID = ran() % map->players[it->first].allowedFactions.size(); | ||||
| 			auto iter = map->players[it->first].allowedFactions.begin(); | ||||
| 			std::advance(iter, randomID); | ||||
|  | ||||
| 			it->second.castle = *iter; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -81,6 +81,8 @@ void CModHandler::loadConfigFromFile (std::string name) | ||||
| 		tlog3 << "\t\tFound mod file: " << entry.getResourceName() << "\n"; | ||||
| 		const JsonNode config ((char*)textData.get(), stream->getSize()); | ||||
|  | ||||
| 		VLC->townh->loadFactions(config["factions"]); | ||||
|  | ||||
| 		const JsonNode *value = &config["creatures"]; | ||||
| 		BOOST_FOREACH (auto creature, value->Vector()) | ||||
| 		{ | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "VCMI_Lib.h" | ||||
| #include "CCreatureHandler.h" | ||||
| #include "CArtHandler.h" | ||||
| #include "CTownHandler.h" | ||||
|  | ||||
| /* | ||||
|  * CModHandler.h, part of VCMI engine | ||||
|   | ||||
| @@ -189,10 +189,6 @@ class DLL_LINKAGE CTownHandler | ||||
|  | ||||
| 	void loadPuzzle(CFaction & faction, const JsonNode & source); | ||||
|  | ||||
| 	/// main loading function, accepts merged JSON source and add all entries from it into game | ||||
| 	/// all entries in JSON should be checked for validness before using this function | ||||
| 	void loadFactions(const JsonNode & source); | ||||
|  | ||||
| 	/// load all available data from h3 txt(s) into json structure using format similar to vcmi configs | ||||
| 	/// returns 2d array [townID] [buildID] of buildings | ||||
| 	void loadLegacyData(JsonNode & dest); | ||||
| @@ -203,7 +199,11 @@ public: | ||||
|  | ||||
| 	CTownHandler(); //c-tor, set pointer in VLC to this | ||||
|  | ||||
| 	/// "entry point" for towns loading. | ||||
| 	/// main loading function for mods, accepts merged JSON source and add all entries from it into game | ||||
| 	/// all entries in JSON should be checked for validness before using this function | ||||
| 	void loadFactions(const JsonNode & source); | ||||
|  | ||||
| 	/// "entry point" for loading of OH3 town. | ||||
| 	/// reads legacy txt's from H3 + vcmi json, merges them | ||||
| 	/// and loads resulting structure to game using loadTowns method | ||||
| 	/// in future may require loaded Creature Handler | ||||
|   | ||||
| @@ -543,31 +543,38 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) | ||||
| { | ||||
| 	ERROR_RET_VAL_IF(!canGetFullInfo(t), "Town is not owned!", -1); | ||||
|  | ||||
| 	int ret = EBuildingState::ALLOWED; | ||||
| 	if(t->builded >= VLC->modh->settings.MAX_BUILDING_PER_TURN) | ||||
| 		ret = EBuildingState::CANT_BUILD_TODAY; //building limit | ||||
|  | ||||
| 	CBuilding * pom = t->town->buildings[ID]; | ||||
|  | ||||
| 	if(!pom) | ||||
| 		return EBuildingState::BUILDING_ERROR; | ||||
|  | ||||
| 	//checking resources | ||||
| 	if(!pom->resources.canBeAfforded(getPlayer(t->tempOwner)->resources)) | ||||
| 		ret = EBuildingState::NO_RESOURCES; //lack of res | ||||
| 	if(t->hasBuilt(ID))	//already built | ||||
| 		return EBuildingState::ALREADY_PRESENT; | ||||
|  | ||||
| 	//can we build it? | ||||
| 	if(t->forbiddenBuildings.find(ID)!=t->forbiddenBuildings.end()) | ||||
| 		return EBuildingState::FORBIDDEN; //forbidden | ||||
|  | ||||
| 	//checking for requirements | ||||
| 	std::set<int> reqs = getBuildingRequiments(t, ID);//getting all requirements | ||||
|  | ||||
| 	bool notAllBuilt = false; | ||||
| 	for( std::set<int>::iterator ri  =  reqs.begin(); ri != reqs.end(); ri++ ) | ||||
| 	{ | ||||
| 		if(!t->hasBuilt(*ri)) | ||||
| 			ret = EBuildingState::PREREQUIRES; //lack of requirements - cannot build | ||||
| 		if(!t->hasBuilt(*ri)) //lack of requirements - cannot build | ||||
| 		{ | ||||
| 			if(vstd::contains(t->forbiddenBuildings, *ri)) // not built requirement forbidden - same goes to this build | ||||
| 				return EBuildingState::FORBIDDEN; | ||||
| 			else | ||||
| 				notAllBuilt = true; // no return here - we need to check if any required builds are forbidden | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//can we build it? | ||||
| 	if(t->forbiddenBuildings.find(ID)!=t->forbiddenBuildings.end()) | ||||
| 		ret = EBuildingState::FORBIDDEN; //forbidden | ||||
| 	if(t->builded >= VLC->modh->settings.MAX_BUILDING_PER_TURN) | ||||
| 		return EBuildingState::CANT_BUILD_TODAY; //building limit | ||||
|  | ||||
| 	if (notAllBuilt) | ||||
| 		return EBuildingState::PREREQUIRES; | ||||
|  | ||||
| 	if(ID == 13) //capitol | ||||
| 	{ | ||||
| @@ -578,8 +585,7 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) | ||||
| 			{ | ||||
| 				if(t->hasBuilt(EBuilding::CAPITOL)) | ||||
| 				{ | ||||
| 					ret = EBuildingState::HAVE_CAPITAL; //no more than one capitol | ||||
| 					break; | ||||
| 					return EBuildingState::HAVE_CAPITAL; //no more than one capitol | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -589,12 +595,14 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) | ||||
| 		const TerrainTile *tile = getTile(t->bestLocation(), false); | ||||
| 		 | ||||
| 		if(!tile || tile->tertype != TerrainTile::water ) | ||||
| 			ret = EBuildingState::NO_WATER; //lack of water | ||||
| 			return EBuildingState::NO_WATER; //lack of water | ||||
| 	} | ||||
|  | ||||
| 	if(t->hasBuilt(ID))	//already built | ||||
| 		ret = EBuildingState::ALREADY_PRESENT; | ||||
| 	return ret; | ||||
| 	//checking resources | ||||
| 	if(!pom->resources.canBeAfforded(getPlayer(t->tempOwner)->resources)) | ||||
| 		return EBuildingState::NO_RESOURCES; //lack of res | ||||
|  | ||||
| 	return EBuildingState::ALLOWED; | ||||
| } | ||||
|  | ||||
| std::set<int> CGameInfoCallback::getBuildingRequiments( const CGTownInstance *t, int ID ) | ||||
|   | ||||
| @@ -228,10 +228,13 @@ void CMapHeader::loadPlayerInfo( int &pom, const ui8 * bufor, int &i ) | ||||
| 			players[pom].p7= -1; | ||||
|  | ||||
| 		//factions this player can choose | ||||
| 		players[pom].allowedFactions = 0; | ||||
| 		players[pom].allowedFactions += bufor[i++]; | ||||
| 		ui16 allowedFactions = bufor[i++]; | ||||
| 		if(version != RoE) | ||||
| 			players[pom].allowedFactions += (bufor[i++])*256; | ||||
| 			allowedFactions += (bufor[i++])*256; | ||||
|  | ||||
| 		for (size_t fact=0; fact<16; fact++) | ||||
| 			if (allowedFactions & (1 << fact)) | ||||
| 				players[pom].allowedFactions.insert(fact); | ||||
|  | ||||
| 		players[pom].isFactionRandom = bufor[i++]; | ||||
| 		players[pom].hasMainTown = bufor[i++]; | ||||
|   | ||||
| @@ -96,7 +96,7 @@ struct DLL_LINKAGE PlayerInfo | ||||
| 	ui8 canHumanPlay; | ||||
| 	ui8 canComputerPlay; | ||||
| 	ui32 AITactic; //(00 - random, 01 -  warrior, 02 - builder, 03 - explorer) | ||||
| 	ui32 allowedFactions; //(01 - castle; 02 - rampart; 04 - tower; 08 - inferno; 16 - necropolis; 32 - dungeon; 64 - stronghold; 128 - fortress; 256 - conflux); | ||||
| 	std::set<ui32> allowedFactions; //set with factions player can play with | ||||
| 	ui8 isFactionRandom; | ||||
| 	ui32 mainHeroPortrait; //it's ID of hero with chosen portrait; 255 if standard | ||||
| 	std::string mainHeroName; | ||||
| @@ -108,7 +108,7 @@ struct DLL_LINKAGE PlayerInfo | ||||
| 	ui8 generateHero; | ||||
|  | ||||
| 	PlayerInfo(): p7(0), p8(0), p9(0), canHumanPlay(0), canComputerPlay(0), | ||||
| 		AITactic(0), allowedFactions(0), isFactionRandom(0), | ||||
| 		AITactic(0), isFactionRandom(0), | ||||
| 		mainHeroPortrait(0), hasMainTown(0), generateHeroAtMainTown(0), | ||||
| 		team(255), generateHero(0) {}; | ||||
|  | ||||
| @@ -117,7 +117,7 @@ struct DLL_LINKAGE PlayerInfo | ||||
| 		si8 ret = -2; | ||||
| 		for (int j = 0; j < GameConstants::F_NUMBER  &&  ret != -1; j++) //we start with none and find matching faction. if more than one, then set to random | ||||
| 		{ | ||||
| 			if((1 << j) & allowedFactions) | ||||
| 			if(vstd::contains(allowedFactions, j)) | ||||
| 			{ | ||||
| 				if (ret >= 0) //we've already assigned a castle and another one is possible -> set random and let player choose | ||||
| 					ret = -1; //breaks | ||||
|   | ||||
		Reference in New Issue
	
	Block a user