mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #1871 from IvanSavenko/h3m_reader_refactoring
Hota H3M parsing support (1.3)
This commit is contained in:
		| @@ -294,7 +294,7 @@ bool BuildAnalyzer::hasAnyBuilding(int32_t alignment, BuildingID bid) const | ||||
| { | ||||
| 	for(auto tdi : developmentInfos) | ||||
| 	{ | ||||
| 		if(tdi.town->alignment == alignment && tdi.town->hasBuilt(bid)) | ||||
| 		if(tdi.town->subID == alignment && tdi.town->hasBuilt(bid)) | ||||
| 			return true; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -848,7 +848,7 @@ public: | ||||
|  | ||||
| uint64_t RewardEvaluator::getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const | ||||
| { | ||||
| 	if(ai->buildAnalyzer->hasAnyBuilding(town->alignment, bi.id)) | ||||
| 	if(ai->buildAnalyzer->hasAnyBuilding(town->subID, bi.id)) | ||||
| 		return 0; | ||||
|  | ||||
| 	auto creaturesToUpgrade = ai->armyManager->getTotalCreaturesAvailable(bi.baseCreatureID); | ||||
|   | ||||
| @@ -117,6 +117,9 @@ void init() | ||||
|  | ||||
| 	logGlobal->info("Initializing VCMI_Lib: %d ms", tmh.getDiff()); | ||||
|  | ||||
| 	// Debug code to load all maps on start | ||||
| 	//ClientCommandManager commandController; | ||||
| 	//commandController.processCommand("convert txt", false); | ||||
| } | ||||
|  | ||||
| static void prog_version() | ||||
|   | ||||
| @@ -439,7 +439,8 @@ void ClientCommandManager::processCommand(const std::string &message, bool calle | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		printCommandMessage("Command not found :(", ELogLevel::ERROR); | ||||
| 		if (!commandName.empty() && !vstd::iswithin(commandName[0], 0, ' ')) // filter-out debugger/IDE noise | ||||
| 			printCommandMessage("Command not found :(", ELogLevel::ERROR); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -570,6 +570,9 @@ bool BattleInterface::makingTurn() const | ||||
|  | ||||
| void BattleInterface::endAction(const BattleAction* action) | ||||
| { | ||||
| 	// it is possible that tactics mode ended while opening music is still playing | ||||
| 	waitForAnimations(); | ||||
|  | ||||
| 	const CStack *stack = curInt->cb->battleGetStackByID(action->stackNumber); | ||||
|  | ||||
| 	// Activate stack from stackToActivate because this might have been temporary disabled, e.g., during spell cast | ||||
|   | ||||
| @@ -327,7 +327,7 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her | ||||
| 	if(!hero->type->battleImage.empty()) | ||||
| 		animationPath = hero->type->battleImage; | ||||
| 	else | ||||
| 	if(hero->sex) | ||||
| 	if(hero->gender == EHeroGender::FEMALE) | ||||
| 		animationPath = hero->type->heroClass->imageBattleFemale; | ||||
| 	else | ||||
| 		animationPath = hero->type->heroClass->imageBattleMale; | ||||
|   | ||||
| @@ -333,7 +333,7 @@ void SelectionTab::filter(int size, bool selectFirst) | ||||
| 	{ | ||||
| 		for(auto elem : allItems) | ||||
| 		{ | ||||
| 			if(elem->mapHeader && elem->mapHeader->version && (!size || elem->mapHeader->width == size)) | ||||
| 			if(elem->mapHeader && (!size || elem->mapHeader->width == size)) | ||||
| 				curItems.push_back(elem); | ||||
| 		} | ||||
| 	} | ||||
| @@ -537,9 +537,9 @@ void SelectionTab::parseMaps(const std::unordered_set<ResourceID> & files) | ||||
| 			auto mapInfo = std::make_shared<CMapInfo>(); | ||||
| 			mapInfo->mapInit(file.getName()); | ||||
|  | ||||
| 			// ignore unsupported map versions (e.g. WoG maps without WoG) | ||||
| 			// but accept VCMI maps | ||||
| 			if((mapInfo->mapHeader->version >= EMapFormat::VCMI) || (mapInfo->mapHeader->version <= CGI->settings()->getInteger(EGameSettings::TEXTS_MAP_VERSION))) | ||||
| 			EMapFormat maxSupported = static_cast<EMapFormat>(CGI->settings()->getInteger(EGameSettings::TEXTS_MAP_VERSION)); | ||||
|  | ||||
| 			if(mapInfo->mapHeader->version == EMapFormat::VCMI || mapInfo->mapHeader->version <= maxSupported) | ||||
| 				allItems.push_back(mapInfo); | ||||
| 		} | ||||
| 		catch(std::exception & e) | ||||
|   | ||||
| @@ -335,7 +335,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded) | ||||
|  | ||||
| 	formations->resetCallback(); | ||||
| 	//setting formations | ||||
| 	formations->setSelected(curHero->formation); | ||||
| 	formations->setSelected(curHero->formation == EArmyFormation::TIGHT ? 1 : 0); | ||||
| 	formations->addCallback([=](int value){ LOCPLINT->cb->setFormation(curHero, value);}); | ||||
|  | ||||
| 	morale->set(&heroWArt); | ||||
|   | ||||
| @@ -77,7 +77,9 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) | ||||
| 		${MAIN_LIB_DIR}/mapping/CMapOperation.cpp | ||||
| 		${MAIN_LIB_DIR}/mapping/CMapService.cpp | ||||
| 		${MAIN_LIB_DIR}/mapping/MapEditUtils.cpp | ||||
| 		${MAIN_LIB_DIR}/mapping/MapFeaturesH3M.cpp | ||||
| 		${MAIN_LIB_DIR}/mapping/MapFormatH3M.cpp | ||||
| 		${MAIN_LIB_DIR}/mapping/MapReaderH3M.cpp | ||||
| 		${MAIN_LIB_DIR}/mapping/MapFormatJson.cpp | ||||
|  | ||||
| 		${MAIN_LIB_DIR}/registerTypes/RegisterTypes.cpp | ||||
| @@ -347,7 +349,9 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) | ||||
| 		${MAIN_LIB_DIR}/mapping/CMapOperation.h | ||||
| 		${MAIN_LIB_DIR}/mapping/CMapService.h | ||||
| 		${MAIN_LIB_DIR}/mapping/MapEditUtils.h | ||||
| 		${MAIN_LIB_DIR}/mapping/MapFeaturesH3M.h | ||||
| 		${MAIN_LIB_DIR}/mapping/MapFormatH3M.h | ||||
| 		${MAIN_LIB_DIR}/mapping/MapReaderH3M.h | ||||
| 		${MAIN_LIB_DIR}/mapping/MapFormatJson.h | ||||
|  | ||||
| 		${MAIN_LIB_DIR}/registerTypes/RegisterTypes.h | ||||
|   | ||||
							
								
								
									
										379
									
								
								config/objects/hotaObjects.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								config/objects/hotaObjects.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,379 @@ | ||||
| // REFERENCE ONLY - THIS CONFIG FILE IS NOT USED BY VCMI | ||||
| // CONTAINS LIST OF HOTA OBJECTS AND THEIR H3M IDENTIFIERS | ||||
|  | ||||
| { | ||||
| 	"boat" : { | ||||
| 		"lastReservedIndex" : 5, | ||||
| 		"types" : { | ||||
| 			//NOTE: H3 range: 0-2 | ||||
| 			"hotaBoat4" : { | ||||
| 				"index" : 3 | ||||
| 			}, | ||||
| 			"hotaBoat5" : { | ||||
| 				"index" : 4 | ||||
| 			}, | ||||
| 			"hotaBoat6" : { | ||||
| 				"index" : 5 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hillFort" : { | ||||
| 		"lastReservedIndex" : 1, | ||||
| 		"types" : { | ||||
| 			"hotaHillFort" : { | ||||
| 				"index" : 1 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"schoolOfMagic" : { | ||||
| 		"lastReservedIndex" : 1, | ||||
| 		"types" : { | ||||
| 			"hotaSchoolOfMagic" : { | ||||
| 				"index" : 1 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"creatureBank" : { | ||||
| 		"lastReservedIndex" : 26, | ||||
| 		"types" : { | ||||
| 			//NOTE: H3 range: 0-6 | ||||
| 			"hotaBeholderSanctuary" : { | ||||
| 				"index" : 21 | ||||
| 			}, | ||||
| 			"hotaTempleOfTheSea" : { | ||||
| 				"index" : 22 | ||||
| 			}, | ||||
| 			"hotaPirateCave" : { | ||||
| 				"index" : 23 | ||||
| 			}, | ||||
| 			"hotaMansion" : { | ||||
| 				"index" : 24 | ||||
| 			}, | ||||
| 			"hotaSpit" : { | ||||
| 				"index" : 25 | ||||
| 			}, | ||||
| 			"hotaRedTower" : { | ||||
| 				"index" : 26 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaIce" : { | ||||
| 		"index" : 40, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 7, | ||||
| 		"types" : { | ||||
| 			"hotaIce1" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaIce2" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaIce3" : { | ||||
| 				"index" : 2 | ||||
| 			}, | ||||
| 			"hotaIce4" : { | ||||
| 				"index" : 3 | ||||
| 			}, | ||||
| 			"hotaIce5" : { | ||||
| 				"index" : 4 | ||||
| 			}, | ||||
| 			"hotaIce6" : { | ||||
| 				"index" : 5 | ||||
| 			}, | ||||
| 			"hotaIce7" : { | ||||
| 				"index" : 6 | ||||
| 			} | ||||
| 			"hotaIce8" : { | ||||
| 				"index" : 7 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 	 | ||||
| 	"hotaStatic" : { | ||||
| 		"index" :139, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 26, | ||||
| 		"types" : { | ||||
| 			"hotaCrates0" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaCrates1" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaBag" : { | ||||
| 				"index" : 2 | ||||
| 			}, | ||||
| 			"hotaCrates3" : { | ||||
| 				"index" : 3 | ||||
| 			}, | ||||
| 			"hotaJaws" : { | ||||
| 				"index" : 4 | ||||
| 			}, | ||||
| 			"hotaRopesPile" : { | ||||
| 				"index" : 5 | ||||
| 			}, | ||||
| 			"hotaFrog" : { | ||||
| 				"index" : 6 | ||||
| 			}, | ||||
| 			"hotaFrogs" : { | ||||
| 				"index" : 7 | ||||
| 			}, | ||||
| 			"hotaHen" : { | ||||
| 				"index" : 8 | ||||
| 			}, | ||||
| 			"hotaRooster" : { | ||||
| 				"index" : 9 | ||||
| 			}, | ||||
| 			"hotaPortu" : { | ||||
| 				"index" : 10 | ||||
| 			}, | ||||
| 			"hotaDestroyedMercenaryCamp" : { | ||||
| 				"index" : 11 | ||||
| 			}, | ||||
| 			"hotaDestroyedFountain" : { | ||||
| 				"index" : 12 | ||||
| 			}, | ||||
| 			"hotaPig" : { | ||||
| 				"index" : 13 | ||||
| 			}, | ||||
| 			"hotaHornAltar" : { | ||||
| 				"index" : 14 | ||||
| 			}, | ||||
| 			"hotaBoatWreckage" : { | ||||
| 				"index" : 15 | ||||
| 			}, | ||||
| 			"hotaPalisade" : { | ||||
| 				"index" : 16 | ||||
| 			}, | ||||
| 			"hotaWaterfall" : { | ||||
| 				"index" : 17 | ||||
| 			}, | ||||
| 			"hotaFlames" : { | ||||
| 				"index" : 18 | ||||
| 			}, | ||||
| 			"hotaDestroyedUndegroundGate" : { | ||||
| 				"index" : 19 | ||||
| 			}, | ||||
| 			"hotaPredatoryPlant" : { | ||||
| 				"index" : 20 | ||||
| 			}, | ||||
| 			"hotaBridge" : { | ||||
| 				"index" : 21 | ||||
| 			}, | ||||
| 			"hotaBones" : { | ||||
| 				"index" : 22 | ||||
| 			}, | ||||
| 			"hotaPond" : { | ||||
| 				"index" : 24 | ||||
| 			}, | ||||
| 			"hotaPillar" : { | ||||
| 				"index" : 25 | ||||
| 			}, | ||||
| 			"hotaPond2" : { | ||||
| 				"index" : 26 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaNature" : { | ||||
| 		"index" :140, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 8, | ||||
| 		"types" : { | ||||
| 			"hotaWaterball" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaRock" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaTreesTropical" : { | ||||
| 				"index" : 2 | ||||
| 			}, | ||||
| 			"hotaIce" : { | ||||
| 				"index" : 3 | ||||
| 			}, | ||||
| 			"hotaMissing" : { | ||||
| 				"index" : 4 | ||||
| 			}, | ||||
| 			"hotaSnowHill" : { | ||||
| 				"index" : 5 | ||||
| 			}, | ||||
| 			"hotaMountains" : { | ||||
| 				"index" : 6 | ||||
| 			}, | ||||
| 			"hotaTrees" : { | ||||
| 				"index" : 7 | ||||
| 			}, | ||||
| 			"hotaLakeWater" : { | ||||
| 				"index" : 8 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaTerrains" : { | ||||
| 		"index" :141, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 2, | ||||
| 		"types" : { | ||||
| 			"hotaIce" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaDunes" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaFieldsOfGlory" : { | ||||
| 				"index" : 2 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaWarehouses" : { | ||||
| 		"index" :142, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 6, | ||||
| 		"types" : { | ||||
| 			"hotaWarehouseWood" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaWarehouseMercury" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaWarehouseOre" : { | ||||
| 				"index" : 2 | ||||
| 			}, | ||||
| 			"hotaWarehouseSulfur" : { | ||||
| 				"index" : 3 | ||||
| 			}, | ||||
| 			"hotaWarehouseCrystal" : { | ||||
| 				"index" : 4 | ||||
| 			}, | ||||
| 			"hotaWarehouseGems" : { | ||||
| 				"index" : 5 | ||||
| 			}, | ||||
| 			"hotaWarehouseGold" : { | ||||
| 				"index" : 6 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaInteractive" : { | ||||
| 		"index" :144, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 11, | ||||
| 		"types" : { | ||||
| 			"hotaTempleOfLoyalty" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaSkeletonTransformer" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaMagicArena" : { | ||||
| 				"index" : 2 | ||||
| 			}, | ||||
| 			"hotaWateringPlace" : { | ||||
| 				"index" : 3 | ||||
| 			}, | ||||
| 			"hotaMineralSpring" : { | ||||
| 				"index" : 4 | ||||
| 			}, | ||||
| 			"hotaHermitShack" : { | ||||
| 				"index" : 5 | ||||
| 			}, | ||||
| 			"hotaGazebo" : { | ||||
| 				"index" : 6 | ||||
| 			}, | ||||
| 			"hotaJunkman" : { | ||||
| 				"index" : 7 | ||||
| 			}, | ||||
| 			"hotaDerrick" : { | ||||
| 				"index" : 8 | ||||
| 			}, | ||||
| 			"hotaWarlockLab" : { | ||||
| 				"index" : 9 | ||||
| 			}, | ||||
| 			"hotaProspector" : { | ||||
| 				"index" : 10 | ||||
| 			}, | ||||
| 			"hotaTrailblazer" : { | ||||
| 				"index" : 11 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaWaterObjects" : { | ||||
| 		"index" :145, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 3, | ||||
| 		"types" : { | ||||
| 			"hotaGenieBottle" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaBarrelWater" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaCrateWater" : { | ||||
| 				"index" : 2 | ||||
| 			}, | ||||
| 			"hotaManaBottle" : { | ||||
| 				"index" : 3 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaInteractive2" : { | ||||
| 		"index" :146, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 3, | ||||
| 		"types" : { | ||||
| 			"hotaWarAcademy" : { | ||||
| 				"index" : 0 | ||||
| 			}, | ||||
| 			"hotaObservatory" : { | ||||
| 				"index" : 1 | ||||
| 			}, | ||||
| 			"hotaAltarOfMana" : { | ||||
| 				"index" : 2 | ||||
| 			} | ||||
| 			"hotaTownGates" : { | ||||
| 				"index" : 3 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 	 | ||||
| 	"hotaStatic2" : { | ||||
| 		"index" :154, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 1, | ||||
| 		"types" : { | ||||
| 			"hotaOilLake" : { | ||||
| 				"index" : 0 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaLyuc" : { | ||||
| 		"index" :160, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 1, | ||||
| 		"types" : { | ||||
| 			"hotaLyuc" : { | ||||
| 				"index" : 0 | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	"hotaLyuc2" : { | ||||
| 		"index" :204, | ||||
| 		"handler": "static", | ||||
| 		"lastReservedIndex" : 1, | ||||
| 		"types" : { | ||||
| 			"hotaLyuc2" : { | ||||
| 				"index" : 0 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -418,7 +418,10 @@ int CCreatureSet::stacksCount() const | ||||
|  | ||||
| void CCreatureSet::setFormation(bool tight) | ||||
| { | ||||
| 	formation = tight; | ||||
| 	if (tight) | ||||
| 		formation = EArmyFormation::TIGHT; | ||||
| 	else | ||||
| 		formation = EArmyFormation::LOOSE; | ||||
| } | ||||
|  | ||||
| void CCreatureSet::setStackCount(const SlotID & slot, TQuantity count) | ||||
| @@ -694,7 +697,6 @@ void CStackInstance::init() | ||||
| 	experience = 0; | ||||
| 	count = 0; | ||||
| 	type = nullptr; | ||||
| 	idRand = -1; | ||||
| 	_armyObj = nullptr; | ||||
| 	setNodeType(STACK_INSTANCE); | ||||
| } | ||||
| @@ -815,8 +817,7 @@ std::string CStackInstance::getQuantityTXT(bool capitalized) const | ||||
|  | ||||
| bool CStackInstance::valid(bool allowUnrandomized) const | ||||
| { | ||||
| 	bool isRand = (idRand != -1); | ||||
| 	if(!isRand) | ||||
| 	if(!randomStack) | ||||
| 	{ | ||||
| 		return (type  &&  type == VLC->creh->objects[type->getId()]); | ||||
| 	} | ||||
| @@ -830,8 +831,6 @@ std::string CStackInstance::nodeName() const | ||||
| 	oss << "Stack of " << count << " of "; | ||||
| 	if(type) | ||||
| 		oss << type->getNamePluralTextID(); | ||||
| 	else if(idRand >= 0) | ||||
| 		oss << "[no type, idRand=" << idRand << "]"; | ||||
| 	else | ||||
| 		oss << "[UNDEFINED TYPE]"; | ||||
|  | ||||
| @@ -888,14 +887,13 @@ void CStackInstance::serializeJson(JsonSerializeFormat & handler) | ||||
|  | ||||
| 	if(handler.saving) | ||||
| 	{ | ||||
| 		if(idRand > -1) | ||||
| 		if(randomStack) | ||||
| 		{ | ||||
| 			int level = idRand / 2; | ||||
|  | ||||
| 			boost::logic::tribool upgraded = (idRand % 2) > 0; | ||||
| 			int level = randomStack->level; | ||||
| 			int upgrade = randomStack->upgrade; | ||||
|  | ||||
| 			handler.serializeInt("level", level, 0); | ||||
| 			handler.serializeBool("upgraded", upgraded); | ||||
| 			handler.serializeInt("upgraded", upgrade, 0); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| @@ -903,13 +901,13 @@ void CStackInstance::serializeJson(JsonSerializeFormat & handler) | ||||
| 		//type set by CStackBasicDescriptor::serializeJson | ||||
| 		if(type == nullptr) | ||||
| 		{ | ||||
| 			int level = 0; | ||||
| 			bool upgraded = false; | ||||
| 			uint8_t level = 0; | ||||
| 			uint8_t upgrade = 0; | ||||
|  | ||||
| 			handler.serializeInt("level", level, 0); | ||||
| 			handler.serializeBool("upgraded", upgraded); | ||||
| 			handler.serializeInt("upgrade", upgrade, 0); | ||||
|  | ||||
| 			idRand = level * 2 + static_cast<int>(upgraded); | ||||
| 			randomStack = RandomStackInfo{ level, upgrade }; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -946,7 +944,6 @@ void CCommanderInstance::init() | ||||
| 	level = 1; | ||||
| 	count = 1; | ||||
| 	type = nullptr; | ||||
| 	idRand = -1; | ||||
| 	_armyObj = nullptr; | ||||
| 	setNodeType (CBonusSystemNode::COMMANDER); | ||||
| 	secondarySkills.resize (ECommander::SPELL_POWER + 1); | ||||
|   | ||||
| @@ -69,11 +69,13 @@ protected: | ||||
| 	const CArmedInstance *_armyObj; //stack must be part of some army, army must be part of some object | ||||
|  | ||||
| public: | ||||
| 	// hlp variable used during loading map, when object (hero or town) have creatures that must have same alignment. | ||||
| 	// idRand < 0 -> normal, non-random creature | ||||
| 	// idRand / 2 -> level | ||||
| 	// idRand % 2 -> upgrade number | ||||
| 	int idRand; | ||||
| 	struct RandomStackInfo | ||||
| 	{ | ||||
| 		uint8_t level; | ||||
| 		uint8_t upgrade; | ||||
| 	}; | ||||
| 	// helper variable used during loading map, when object (hero or town) have creatures that must have same alignment. | ||||
| 	boost::optional<RandomStackInfo> randomStack; | ||||
|  | ||||
| 	const CArmedInstance * const & armyObj; //stack must be part of some army, army must be part of some object | ||||
| 	TExpType experience;//commander needs same amount of exp as hero | ||||
| @@ -200,13 +202,21 @@ public: | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| enum class EArmyFormation : uint8_t | ||||
| { | ||||
| 	LOOSE, | ||||
| 	TIGHT | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CCreatureSet : public IArmyDescriptor //seven combined creatures | ||||
| { | ||||
| 	CCreatureSet(const CCreatureSet &) = delete; | ||||
| 	CCreatureSet &operator=(const CCreatureSet&); | ||||
| public: | ||||
|  | ||||
|  | ||||
| 	TSlots stacks; //slots[slot_id]->> pair(creature_id,creature_quantity) | ||||
| 	ui8 formation = 0; //0 - wide, 1 - tight | ||||
| 	EArmyFormation formation = EArmyFormation::LOOSE; //0 - wide, 1 - tight | ||||
|  | ||||
| 	CCreatureSet() = default; //Should be here to avoid compile errors | ||||
| 	virtual ~CCreatureSet(); | ||||
|   | ||||
| @@ -496,7 +496,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj) | ||||
| 		return std::make_pair(Obj::RESOURCE,getRandomGenerator().nextInt(6)); //now it's OH3 style, use %8 for mithril | ||||
| 	case Obj::RANDOM_TOWN: | ||||
| 		{ | ||||
| 			PlayerColor align = PlayerColor((dynamic_cast<CGTownInstance *>(obj))->alignment); | ||||
| 			PlayerColor align = (dynamic_cast<CGTownInstance *>(obj))->alignmentToPlayer; | ||||
| 			si32 f; // can be negative (for random) | ||||
| 			if(align >= PlayerColor::PLAYER_LIMIT) //same as owner / random | ||||
| 			{ | ||||
|   | ||||
| @@ -420,7 +420,7 @@ CHero * CHeroHandler::loadFromJson(const std::string & scope, const JsonNode & n | ||||
| 	hero->ID = HeroTypeID(index); | ||||
| 	hero->identifier = identifier; | ||||
| 	hero->modScope = scope; | ||||
| 	hero->sex = node["female"].Bool(); | ||||
| 	hero->gender = node["female"].Bool() ? EHeroGender::FEMALE : EHeroGender::MALE; | ||||
| 	hero->special = node["special"].Bool(); | ||||
|  | ||||
| 	VLC->generaltexth->registerString(scope, hero->getNameTextID(), node["texts"]["name"].String()); | ||||
|   | ||||
| @@ -29,6 +29,13 @@ class CRandomGenerator; | ||||
| class JsonSerializeFormat; | ||||
| class BattleField; | ||||
|  | ||||
| enum class EHeroGender : uint8_t | ||||
| { | ||||
| 	MALE = 0, | ||||
| 	FEMALE = 1, | ||||
| 	DEFAULT = 0xff // from h3m, instance has same gender as hero type | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CHero : public HeroType | ||||
| { | ||||
| 	friend class CHeroHandler; | ||||
| @@ -61,7 +68,7 @@ public: | ||||
| 	std::set<SpellID> spells; | ||||
| 	bool haveSpellBook = false; | ||||
| 	bool special = false; // hero is special and won't be placed in game (unless preset on map), e.g. campaign heroes | ||||
| 	ui8 sex = 0; // default sex: 0=male, 1=female | ||||
| 	EHeroGender gender = EHeroGender::MALE; // default sex: 0=male, 1=female | ||||
|  | ||||
| 	/// Graphics | ||||
| 	std::string iconSpecSmall; | ||||
| @@ -104,7 +111,7 @@ public: | ||||
| 		h & specialty; | ||||
| 		h & spells; | ||||
| 		h & haveSpellBook; | ||||
| 		h & sex; | ||||
| 		h & gender; | ||||
| 		h & special; | ||||
| 		h & iconSpecSmall; | ||||
| 		h & iconSpecLarge; | ||||
|   | ||||
| @@ -1079,6 +1079,7 @@ public: | ||||
| 		FIRST_AID_TENT = 6, | ||||
| 		//CENTAUR_AXE = 7, | ||||
| 		//BLACKSHARD_OF_THE_DEAD_KNIGHT = 8, | ||||
| 		VIAL_OF_DRAGON_BLOOD = 127, | ||||
| 		ARMAGEDDONS_BLADE = 128, | ||||
| 		TITANS_THUNDER = 135, | ||||
| 		//CORNUCOPIA = 140, | ||||
|   | ||||
| @@ -414,13 +414,14 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const | ||||
| 		for(auto i = armies[side]->Slots().begin(); i != armies[side]->Slots().end(); i++, k++) | ||||
| 		{ | ||||
| 			std::vector<int> *formationVector = nullptr; | ||||
| 			if(creatureBank) | ||||
| 				formationVector = &creBankFormations[side][formationNo]; | ||||
| 			else if(armies[side]->formation) | ||||
| 			if(armies[side]->formation == EArmyFormation::TIGHT ) | ||||
| 				formationVector = &tightFormations[side][formationNo]; | ||||
| 			else | ||||
| 				formationVector = &looseFormations[side][formationNo]; | ||||
|  | ||||
| 			if(creatureBank) | ||||
| 				formationVector = &creBankFormations[side][formationNo]; | ||||
|  | ||||
| 			BattleHex pos = (k < formationVector->size() ? formationVector->at(k) : 0); | ||||
| 			if(creatureBank && i->second->type->isDoubleWide()) | ||||
| 				pos += side ? BattleHex::LEFT : BattleHex::RIGHT; | ||||
|   | ||||
| @@ -23,14 +23,13 @@ void CArmedInstance::randomizeArmy(int type) | ||||
| { | ||||
| 	for (auto & elem : stacks) | ||||
| 	{ | ||||
| 		int & randID = elem.second->idRand; | ||||
| 		if(randID >= 0) | ||||
| 		if(elem.second->randomStack) | ||||
| 		{ | ||||
| 			int level = randID / 2; | ||||
| 			bool upgrade = randID % 2; | ||||
| 			int level = elem.second->randomStack->level; | ||||
| 			int upgrade = elem.second->randomStack->upgrade; | ||||
| 			elem.second->setType((*VLC->townh)[type]->town->creatures[level][upgrade]); | ||||
|  | ||||
| 			randID = -1; | ||||
| 			elem.second->randomStack = boost::none; | ||||
| 		} | ||||
| 		assert(elem.second->valid(false)); | ||||
| 		assert(elem.second->armyObj == this); | ||||
|   | ||||
| @@ -232,7 +232,7 @@ CGHeroInstance::CGHeroInstance(): | ||||
| 	portrait(UNINITIALIZED_PORTRAIT), | ||||
| 	level(1), | ||||
| 	exp(UNINITIALIZED_EXPERIENCE), | ||||
| 	sex(std::numeric_limits<ui8>::max()), | ||||
| 	gender(EHeroGender::DEFAULT), | ||||
| 	lowestCreatureSpeed(0) | ||||
| { | ||||
| 	setNodeType(HERO); | ||||
| @@ -296,8 +296,8 @@ void CGHeroInstance::initHero(CRandomGenerator & rand) | ||||
| 	if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::DEFAULT, -1)) //set secondary skills to default | ||||
| 		secSkills = type->secSkillsInit; | ||||
|  | ||||
| 	if (sex == 0xFF)//sex is default | ||||
| 		sex = type->sex; | ||||
| 	if (gender == EHeroGender::DEFAULT) | ||||
| 		gender = type->gender; | ||||
|  | ||||
| 	setFormation(false); | ||||
| 	if (!stacksCount()) //standard army//initial army | ||||
| @@ -1468,7 +1468,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler) | ||||
| 	} | ||||
|  | ||||
| 	handler.serializeString("name", nameCustom); | ||||
| 	handler.serializeBool<ui8>("female", sex, 1, 0, 0xFF); | ||||
| 	handler.serializeInt("gender", gender, 0); | ||||
|  | ||||
| 	{ | ||||
| 		const int legacyHeroes = VLC->settings()->getInteger(EGameSettings::TEXTS_HERO); | ||||
| @@ -1619,8 +1619,10 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 			setHeroTypeName(typeName); | ||||
| 	} | ||||
|  | ||||
| 	static const std::vector<std::string> FORMATIONS  =	{ "wide", "tight" }; | ||||
|  | ||||
| 	CCreatureSet::serializeJson(handler, "army", 7); | ||||
| 	handler.serializeBool<ui8>("tightFormation", formation, 1, 0, 0); | ||||
| 	handler.serializeEnum("formation", formation, FORMATIONS); | ||||
|  | ||||
| 	{ | ||||
| 		static constexpr int NO_PATROLING = -1; | ||||
|   | ||||
| @@ -25,6 +25,7 @@ class CGTownInstance; | ||||
| class CMap; | ||||
| struct TerrainTile; | ||||
| struct TurnInfo; | ||||
| enum class EHeroGender : uint8_t; | ||||
|  | ||||
| class CGHeroPlaceholder : public CGObjectInstance | ||||
| { | ||||
| @@ -69,7 +70,7 @@ public: | ||||
| 	si32 mana; // remaining spell points | ||||
| 	std::vector<std::pair<SecondarySkill,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities | ||||
| 	ui32 movement; //remaining movement points | ||||
| 	ui8 sex; | ||||
| 	EHeroGender gender; | ||||
|  | ||||
| 	std::string nameCustom; | ||||
| 	std::string biographyCustom; | ||||
| @@ -312,7 +313,7 @@ public: | ||||
| 		h & mana; | ||||
| 		h & secSkills; | ||||
| 		h & movement; | ||||
| 		h & sex; | ||||
| 		h & gender; | ||||
| 		h & inTownGarrison; | ||||
| 		h & spells; | ||||
| 		h & patrol; | ||||
|   | ||||
| @@ -634,7 +634,7 @@ CGTownInstance::CGTownInstance(): | ||||
| 	builded(0), | ||||
| 	destroyed(0), | ||||
| 	identifier(0), | ||||
| 	alignment(0xff) | ||||
| 	alignmentToPlayer(PlayerColor::NEUTRAL) | ||||
| { | ||||
| 	this->setNodeType(CBonusSystemNode::TOWN); | ||||
| } | ||||
| @@ -1494,9 +1494,11 @@ void CGTownInstance::reset() | ||||
|  | ||||
| void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	static const std::vector<std::string> FORMATIONS  =	{ "wide", "tight" }; | ||||
|  | ||||
| 	CGObjectInstance::serializeJsonOwner(handler); | ||||
| 	CCreatureSet::serializeJson(handler, "army", 7); | ||||
| 	handler.serializeBool<ui8>("tightFormation", formation, 1, 0, 0); | ||||
| 	handler.serializeEnum("tightFormation", formation, FORMATIONS); | ||||
| 	handler.serializeString("name", name); | ||||
|  | ||||
| 	{ | ||||
|   | ||||
| @@ -214,7 +214,7 @@ public: | ||||
| 	si32 destroyed; //how many buildings has been destroyed this turn | ||||
| 	ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero; | ||||
| 	ui32 identifier; //special identifier from h3m (only > RoE maps) | ||||
| 	si32 alignment; | ||||
| 	PlayerColor alignmentToPlayer; // if set to non-neutral, random town will have same faction as specified player | ||||
| 	std::set<BuildingID> forbiddenBuildings; | ||||
| 	std::set<BuildingID> builtBuildings; | ||||
| 	std::set<BuildingID> overriddenBuildings; ///buildings which bonuses are overridden and should not be applied | ||||
| @@ -239,7 +239,7 @@ public: | ||||
| 		h & identifier; | ||||
| 		h & garrisonHero; | ||||
| 		h & visitingHero; | ||||
| 		h & alignment; | ||||
| 		h & alignmentToPlayer; | ||||
| 		h & forbiddenBuildings; | ||||
| 		h & builtBuildings; | ||||
| 		h & bonusValue; | ||||
|   | ||||
| @@ -37,7 +37,11 @@ public: | ||||
| 		MISSION_RESOURCES = 7, | ||||
| 		MISSION_HERO = 8, | ||||
| 		MISSION_PLAYER = 9, | ||||
| 		MISSION_KEYMASTER = 10 | ||||
| 		MISSION_HOTA_MULTI = 10, | ||||
| 		// end of H3 missions | ||||
| 		MISSION_KEYMASTER = 100, | ||||
| 		MISSION_HOTA_HERO_CLASS = 101, | ||||
| 		MISSION_HOTA_REACH_DATE = 102 | ||||
| 	}; | ||||
|  | ||||
| 	enum Eprogress { | ||||
|   | ||||
| @@ -664,23 +664,13 @@ void CGMine::initObj(CRandomGenerator & rand) | ||||
| 		auto * troglodytes = new CStackInstance(CreatureID::TROGLODYTES, howManyTroglodytes); | ||||
| 		putStack(SlotID(0), troglodytes); | ||||
|  | ||||
| 		//after map reading tempOwner placeholds bitmask for allowed resources | ||||
| 		std::vector<GameResID> possibleResources; | ||||
| 		for (int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++) | ||||
| 			if(tempOwner.getNum() & 1<<i) //NOTE: reuse of tempOwner | ||||
| 				possibleResources.push_back(GameResID(i)); | ||||
|  | ||||
| 		assert(!possibleResources.empty()); | ||||
| 		producedResource = *RandomGeneratorUtil::nextItem(possibleResources, rand); | ||||
| 		tempOwner = PlayerColor::NEUTRAL; | ||||
| 		assert(!abandonedMineResources.empty()); | ||||
| 		producedResource = *RandomGeneratorUtil::nextItem(abandonedMineResources, rand); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		producedResource = GameResID(subID); | ||||
| 		if(tempOwner >= PlayerColor::PLAYER_LIMIT) | ||||
| 			tempOwner = PlayerColor::NEUTRAL; | ||||
| 	} | ||||
|  | ||||
| 	producedQuantity = defaultResProduction(); | ||||
| } | ||||
|  | ||||
| @@ -766,14 +756,11 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 		if(handler.saving) | ||||
| 		{ | ||||
| 			JsonNode node(JsonNode::JsonType::DATA_VECTOR); | ||||
| 			for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++) | ||||
| 			for(auto const & resID : abandonedMineResources) | ||||
| 			{ | ||||
| 				if(tempOwner.getNum() & 1<<i) | ||||
| 				{ | ||||
| 					JsonNode one(JsonNode::JsonType::DATA_STRING); | ||||
| 					one.String() = GameConstants::RESOURCE_NAMES[i]; | ||||
| 					node.Vector().push_back(one); | ||||
| 				} | ||||
| 				JsonNode one(JsonNode::JsonType::DATA_STRING); | ||||
| 				one.String() = GameConstants::RESOURCE_NAMES[resID]; | ||||
| 				node.Vector().push_back(one); | ||||
| 			} | ||||
| 			handler.serializeRaw("possibleResources", node, boost::none); | ||||
| 		} | ||||
| @@ -781,32 +768,17 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 		{ | ||||
| 			auto guard = handler.enterArray("possibleResources"); | ||||
| 			const JsonNode & node = handler.getCurrent(); | ||||
| 			std::set<int> possibleResources; | ||||
| 			std::set<int> abandonedMineResources; | ||||
|  | ||||
| 			if(node.getType() != JsonNode::JsonType::DATA_VECTOR || node.Vector().empty()) | ||||
| 			auto names = node.convertTo<std::vector<std::string>>(); | ||||
|  | ||||
| 			for(const std::string & s : names) | ||||
| 			{ | ||||
| 				//assume all allowed | ||||
| 				for(int i = static_cast<int>(EGameResID::WOOD); i < static_cast<int>(EGameResID::GOLD); i++) | ||||
| 					possibleResources.insert(i); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				auto names = node.convertTo<std::vector<std::string>>(); | ||||
|  | ||||
| 				for(const std::string & s : names) | ||||
| 				{ | ||||
| 					int raw_res = vstd::find_pos(GameConstants::RESOURCE_NAMES, s); | ||||
| 					if(raw_res < 0) | ||||
| 						logGlobal->error("Invalid resource name: %s", s); | ||||
| 					else | ||||
| 						possibleResources.insert(raw_res); | ||||
| 				} | ||||
|  | ||||
| 				int tmp = 0; | ||||
|  | ||||
| 				for(int r : possibleResources) | ||||
| 					tmp |=  (1<<r); | ||||
| 				tempOwner = PlayerColor(tmp); | ||||
| 				int raw_res = vstd::find_pos(GameConstants::RESOURCE_NAMES, s); | ||||
| 				if(raw_res < 0) | ||||
| 					logGlobal->error("Invalid resource name: %s", s); | ||||
| 				else | ||||
| 					abandonedMineResources.insert(raw_res); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -1422,12 +1394,17 @@ void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler) | ||||
|  | ||||
| void CGWitchHut::initObj(CRandomGenerator & rand) | ||||
| { | ||||
| 	if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file | ||||
| 	if (allowedAbilities.empty()) //this can happen for RMG and RoE maps. | ||||
| 	{ | ||||
| 		// Necromancy can't be learned on random maps | ||||
| 		for(int i = 0; i < VLC->skillh->size(); i++) | ||||
| 			if(VLC->skillh->getByIndex(i)->getId() != SecondarySkill::NECROMANCY) | ||||
| 				allowedAbilities.push_back(i); | ||||
| 		auto defaultAllowed = VLC->skillh->getDefaultAllowed(); | ||||
|  | ||||
| 		// Necromancy and Leadership can't be learned by default | ||||
| 		defaultAllowed[SecondarySkill::NECROMANCY] = false; | ||||
| 		defaultAllowed[SecondarySkill::LEADERSHIP] = false; | ||||
|  | ||||
| 		for(int i = 0; i < defaultAllowed.size(); i++) | ||||
| 			if (defaultAllowed[i] && cb->isAllowed(2, i)) | ||||
| 				allowedAbilities.insert(i); | ||||
| 	} | ||||
| 	ability = *RandomGeneratorUtil::nextItem(allowedAbilities, rand); | ||||
| } | ||||
| @@ -1503,7 +1480,7 @@ void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 		allowedAbilities.clear(); | ||||
| 		for(si32 i = 0; i < skillCount; ++i) | ||||
| 			if(temp[i]) | ||||
| 				allowedAbilities.push_back(i); | ||||
| 				allowedAbilities.insert(i); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -131,7 +131,7 @@ protected: | ||||
| class DLL_LINKAGE CGWitchHut : public CTeamVisited | ||||
| { | ||||
| public: | ||||
| 	std::vector<si32> allowedAbilities; | ||||
| 	std::set<si32> allowedAbilities; | ||||
| 	ui32 ability; | ||||
|  | ||||
| 	std::string getHoverText(PlayerColor player) const override; | ||||
| @@ -265,6 +265,7 @@ class DLL_LINKAGE CGMine : public CArmedInstance | ||||
| public: | ||||
| 	GameResID producedResource; | ||||
| 	ui32 producedQuantity; | ||||
| 	std::set<GameResID> abandonedMineResources; | ||||
|  | ||||
| private: | ||||
| 	void onHeroVisit(const CGHeroInstance * h) const override; | ||||
| @@ -285,6 +286,7 @@ public: | ||||
| 		h & static_cast<CArmedInstance&>(*this); | ||||
| 		h & producedResource; | ||||
| 		h & producedQuantity; | ||||
| 		h & abandonedMineResources; | ||||
| 	} | ||||
| 	ui32 defaultResProduction() const; | ||||
|  | ||||
|   | ||||
| @@ -35,7 +35,7 @@ SHeroName::SHeroName() : heroId(-1) | ||||
|  | ||||
| PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false), | ||||
| 	aiTactic(EAiTactic::RANDOM), isFactionRandom(false), hasRandomHero(false), mainCustomHeroPortrait(-1), mainCustomHeroId(-1), hasMainTown(false), | ||||
| 	generateHeroAtMainTown(false), posOfMainTown(-1), team(TeamID::NO_TEAM), /* following are unused */ generateHero(false), p7(0), powerPlaceholders(-1) | ||||
| 	generateHeroAtMainTown(false), posOfMainTown(-1), team(TeamID::NO_TEAM) | ||||
| { | ||||
| 	allowedFactions = VLC->townh->getAllowedFactions(); | ||||
| } | ||||
|   | ||||
| @@ -86,17 +86,9 @@ struct DLL_LINKAGE PlayerInfo | ||||
| 	int3 posOfMainTown; | ||||
| 	TeamID team; /// The default value NO_TEAM | ||||
|  | ||||
|  | ||||
| 	bool generateHero; /// Unused. | ||||
| 	si32 p7; /// Unknown and unused. | ||||
| 	/// Unused. Count of hero placeholders containing hero type. | ||||
| 	/// WARNING: powerPlaceholders sometimes gives false 0 (eg. even if there is one placeholder), maybe different meaning ??? | ||||
| 	ui8 powerPlaceholders; | ||||
|  | ||||
| 	template <typename Handler> | ||||
| 	void serialize(Handler & h, const int version) | ||||
| 	{ | ||||
| 		h & p7; | ||||
| 		h & hasRandomHero; | ||||
| 		h & mainCustomHeroId; | ||||
| 		h & canHumanPlay; | ||||
| @@ -111,7 +103,6 @@ struct DLL_LINKAGE PlayerInfo | ||||
| 		h & generateHeroAtMainTown; | ||||
| 		h & posOfMainTown; | ||||
| 		h & team; | ||||
| 		h & generateHero; | ||||
| 		h & mainHeroInstance; | ||||
| 	} | ||||
| }; | ||||
| @@ -245,7 +236,7 @@ struct DLL_LINKAGE DisposedHero | ||||
| 	DisposedHero(); | ||||
|  | ||||
| 	ui32 heroId; | ||||
| 	ui16 portrait; /// The portrait id of the hero, 0xFF is default. | ||||
| 	ui32 portrait; /// The portrait id of the hero, -1 is default. | ||||
| 	std::string name; | ||||
| 	ui8 players; /// Who can hire this hero (bitfield). | ||||
|  | ||||
| @@ -259,20 +250,17 @@ struct DLL_LINKAGE DisposedHero | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| namespace EMapFormat | ||||
| { | ||||
| enum EMapFormat: ui8 | ||||
| enum class EMapFormat: uint8_t | ||||
| { | ||||
| 	INVALID = 0, | ||||
| 	//    HEX     DEC | ||||
| 	ROE = 0x0e, // 14 | ||||
| 	AB  = 0x15, // 21 | ||||
| 	SOD = 0x1c, // 28 | ||||
| // HOTA = 0x1e ... 0x20 // 28 ... 30 | ||||
| 	WOG = 0x33,  // 51 | ||||
| 	VCMI = 0xF0 | ||||
| 	//       HEX    DEC | ||||
| 	ROE   = 0x0e, // 14 | ||||
| 	AB    = 0x15, // 21 | ||||
| 	SOD   = 0x1c, // 28 | ||||
| 	HOTA  = 0x20, // 32 | ||||
| 	WOG   = 0x33, // 51 | ||||
| 	VCMI  = 0xF0 | ||||
| }; | ||||
| } | ||||
|  | ||||
| /// The map header holds information about loss/victory condition,map format, version, players, height, width,... | ||||
| class DLL_LINKAGE CMapHeader | ||||
| @@ -293,7 +281,7 @@ public: | ||||
|  | ||||
| 	ui8 levels() const; | ||||
|  | ||||
| 	EMapFormat::EMapFormat version; /// The default value is EMapFormat::SOD. | ||||
| 	EMapFormat version; /// The default value is EMapFormat::SOD. | ||||
| 	si32 height; /// The default value is 72. | ||||
| 	si32 width; /// The default value is 72. | ||||
| 	bool twoLevel; /// The default value is true. | ||||
|   | ||||
| @@ -120,10 +120,11 @@ std::unique_ptr<IMapLoader> CMapService::getMapLoader(std::unique_ptr<CInputStre | ||||
| 			case 0x00088B1F: | ||||
| 				stream = std::unique_ptr<CInputStream>(new CCompressedStream(std::move(stream), true)); | ||||
| 				return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, modName, encoding, stream.get())); | ||||
| 			case EMapFormat::WOG : | ||||
| 			case EMapFormat::AB  : | ||||
| 			case EMapFormat::ROE : | ||||
| 			case EMapFormat::SOD : | ||||
| 			case static_cast<int>(EMapFormat::WOG) : | ||||
| 			case static_cast<int>(EMapFormat::AB)  : | ||||
| 			case static_cast<int>(EMapFormat::ROE) : | ||||
| 			case static_cast<int>(EMapFormat::SOD) : | ||||
| 			case static_cast<int>(EMapFormat::HOTA) : | ||||
| 				return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, modName, encoding, stream.get())); | ||||
| 			default : | ||||
| 				throw std::runtime_error("Unknown map format"); | ||||
|   | ||||
							
								
								
									
										156
									
								
								lib/mapping/MapFeaturesH3M.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								lib/mapping/MapFeaturesH3M.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| /* | ||||
|  * MapFeaturesH3M.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 "MapFeaturesH3M.h" | ||||
|  | ||||
| #include "CMap.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| MapFormatFeaturesH3M MapFormatFeaturesH3M::find(EMapFormat format, uint32_t hotaVersion) | ||||
| { | ||||
| 	switch(format) | ||||
| 	{ | ||||
| 		case EMapFormat::ROE: | ||||
| 			return getFeaturesROE(); | ||||
| 		case EMapFormat::AB: | ||||
| 			return getFeaturesAB(); | ||||
| 		case EMapFormat::SOD: | ||||
| 			return getFeaturesSOD(); | ||||
| 		case EMapFormat::WOG: | ||||
| 			return getFeaturesWOG(); | ||||
| 		case EMapFormat::HOTA: | ||||
| 			return getFeaturesHOTA(hotaVersion); | ||||
| 		default: | ||||
| 			throw std::runtime_error("Invalid map format!"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesROE() | ||||
| { | ||||
| 	MapFormatFeaturesH3M result; | ||||
| 	result.levelROE = true; | ||||
|  | ||||
| 	result.factionsBytes = 1; | ||||
| 	result.heroesBytes = 16; | ||||
| 	result.artifactsBytes = 16; | ||||
| 	result.skillsBytes = 4; | ||||
| 	result.resourcesBytes = 4; | ||||
| 	result.spellsBytes = 9; | ||||
| 	result.buildingsBytes = 6; | ||||
|  | ||||
| 	result.factionsCount = 8; | ||||
| 	result.heroesCount = 128; | ||||
| 	result.heroesPortraitsCount = 128; | ||||
| 	result.artifactsCount = 127; | ||||
| 	result.resourcesCount = 7; | ||||
| 	result.creaturesCount = 118; | ||||
| 	result.spellsCount = 70; | ||||
| 	result.skillsCount = 28; | ||||
| 	result.terrainsCount = 10; | ||||
| 	result.artifactSlotsCount = 18; | ||||
| 	result.buildingsCount = 40; | ||||
|  | ||||
| 	result.heroIdentifierInvalid = 0xff; | ||||
| 	result.artifactIdentifierInvalid = 0xff; | ||||
| 	result.creatureIdentifierInvalid = 0xff; | ||||
| 	result.spellIdentifierInvalid = 0xff; | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesAB() | ||||
| { | ||||
| 	MapFormatFeaturesH3M result = getFeaturesROE(); | ||||
| 	result.levelAB = true; | ||||
|  | ||||
| 	result.factionsBytes = 2; // + Conflux | ||||
| 	result.factionsCount = 9; | ||||
|  | ||||
| 	result.creaturesCount = 144; // + Conflux and new neutrals | ||||
|  | ||||
| 	result.heroesCount = 156; // + Conflux and campaign heroes | ||||
| 	result.heroesPortraitsCount = 163; | ||||
| 	result.heroesBytes = 20; | ||||
|  | ||||
| 	result.artifactsCount = 129; // + Armaggedon Blade and Vial of Dragon Blood | ||||
| 	result.artifactsBytes = 17; | ||||
|  | ||||
| 	result.artifactIdentifierInvalid = 0xffff; // Now uses 2 bytes / object | ||||
| 	result.creatureIdentifierInvalid = 0xffff; // Now uses 2 bytes / object | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesSOD() | ||||
| { | ||||
| 	MapFormatFeaturesH3M result = getFeaturesAB(); | ||||
| 	result.levelSOD = true; | ||||
|  | ||||
| 	result.artifactsCount = 141; // + Combined artifacts | ||||
| 	result.artifactsBytes = 18; | ||||
|  | ||||
| 	result.artifactSlotsCount = 19; // + MISC_5 slot | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesWOG() | ||||
| { | ||||
| 	MapFormatFeaturesH3M result = getFeaturesSOD(); | ||||
| 	result.levelWOG = true; | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA(uint32_t hotaVersion) | ||||
| { | ||||
| 	// even if changes are minimal, we might not be able to parse map header in map selection screen | ||||
| 	// throw exception - to be cached by map selection screen & excluded as invalid | ||||
| 	if(hotaVersion > 3) | ||||
| 		throw std::runtime_error("Invalid map format!"); | ||||
|  | ||||
| 	MapFormatFeaturesH3M result = getFeaturesSOD(); | ||||
| 	result.levelHOTA0 = true; | ||||
| 	result.levelHOTA1 = hotaVersion > 0; | ||||
| 	//result.levelHOTA2 = hotaVersion > 1; // HOTA2 seems to be identical to HOTA1 so far | ||||
| 	result.levelHOTA3 = hotaVersion > 2; | ||||
|  | ||||
| 	result.artifactsBytes = 21; | ||||
| 	result.heroesBytes = 23; | ||||
|  | ||||
| 	result.terrainsCount = 12; // +Highlands +Wasteland | ||||
| 	result.skillsCount = 29; // + Interference | ||||
| 	result.factionsCount = 10; // + Cove | ||||
| 	result.creaturesCount = 171; // + Cove + neutrals | ||||
|  | ||||
| 	if(hotaVersion < 3) | ||||
| 	{ | ||||
| 		result.artifactsCount = 163; // + HotA artifacts | ||||
| 		result.heroesCount = 178; // + Cove | ||||
| 		result.heroesPortraitsCount = 187; // + Cove | ||||
| 	} | ||||
| 	if(hotaVersion == 3) | ||||
| 	{ | ||||
| 		result.artifactsCount = 165; // + HotA artifacts | ||||
| 		result.heroesCount = 179; // + Cove | ||||
| 		result.heroesPortraitsCount = 187; // + Cove | ||||
| 	} | ||||
|  | ||||
| 	assert((result.heroesCount + 7) / 8 == result.heroesBytes); | ||||
| 	assert((result.artifactsCount + 7) / 8 == result.artifactsBytes); | ||||
|  | ||||
| 	result.heroesCount = 179; // + Cove | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
							
								
								
									
										71
									
								
								lib/mapping/MapFeaturesH3M.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lib/mapping/MapFeaturesH3M.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| /* | ||||
|  * MapFeaturesH3M.h, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| enum class EMapFormat : uint8_t; | ||||
|  | ||||
| struct MapFormatFeaturesH3M | ||||
| { | ||||
| public: | ||||
| 	static MapFormatFeaturesH3M find(EMapFormat format, uint32_t hotaVersion); | ||||
| 	static MapFormatFeaturesH3M getFeaturesROE(); | ||||
| 	static MapFormatFeaturesH3M getFeaturesAB(); | ||||
| 	static MapFormatFeaturesH3M getFeaturesSOD(); | ||||
| 	static MapFormatFeaturesH3M getFeaturesWOG(); | ||||
| 	static MapFormatFeaturesH3M getFeaturesHOTA(uint32_t hotaVersion); | ||||
|  | ||||
| 	MapFormatFeaturesH3M() = default; | ||||
|  | ||||
| 	// number of bytes in bitmask of appropriate type | ||||
|  | ||||
| 	int factionsBytes; | ||||
| 	int heroesBytes; | ||||
| 	int artifactsBytes; | ||||
| 	int resourcesBytes; | ||||
| 	int skillsBytes; | ||||
| 	int spellsBytes; | ||||
| 	int buildingsBytes; | ||||
|  | ||||
| 	// total number of elements of appropriate type | ||||
|  | ||||
| 	int factionsCount; | ||||
| 	int heroesCount; | ||||
| 	int heroesPortraitsCount; | ||||
| 	int artifactsCount; | ||||
| 	int resourcesCount; | ||||
| 	int creaturesCount; | ||||
| 	int spellsCount; | ||||
| 	int skillsCount; | ||||
| 	int terrainsCount; | ||||
| 	int artifactSlotsCount; | ||||
| 	int buildingsCount; | ||||
|  | ||||
| 	// identifier that should be treated as "invalid", usually - '-1' | ||||
|  | ||||
| 	int heroIdentifierInvalid; | ||||
| 	int artifactIdentifierInvalid; | ||||
| 	int creatureIdentifierInvalid; | ||||
| 	int spellIdentifierInvalid; | ||||
|  | ||||
| 	// features from which map format are available | ||||
|  | ||||
| 	bool levelROE = false; | ||||
| 	bool levelAB = false; | ||||
| 	bool levelSOD = false; | ||||
| 	bool levelWOG = false; | ||||
| 	bool levelHOTA0 = false; | ||||
| 	bool levelHOTA1 = false; | ||||
| 	bool levelHOTA3 = false; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -11,17 +11,12 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "CMapService.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../ResourceSet.h" | ||||
| #include "../mapObjects/ObjectTemplate.h" | ||||
|  | ||||
| #include "../int3.h" | ||||
|  | ||||
| #include "MapFeaturesH3M.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CGHeroInstance; | ||||
| class CBinaryReader; | ||||
| class MapReaderH3M; | ||||
| class CArtifactInstance; | ||||
| class CGObjectInstance; | ||||
| class CGSeerHut; | ||||
| @@ -30,7 +25,14 @@ class CGTownInstance; | ||||
| class CCreatureSet; | ||||
| class CInputStream; | ||||
| class TextIdentifier; | ||||
| class CGPandoraBox; | ||||
|  | ||||
| class ObjectInstanceID; | ||||
| class BuildingID; | ||||
| class ObjectTemplate; | ||||
| class SpellID; | ||||
| class PlayerColor; | ||||
| class int3; | ||||
|  | ||||
| class DLL_LINKAGE CMapLoaderH3M : public IMapLoader | ||||
| { | ||||
| @@ -61,9 +63,6 @@ public: | ||||
| 	 */ | ||||
| 	std::unique_ptr<CMapHeader> loadMapHeader() override; | ||||
|  | ||||
| 	/** true if you want to enable the map loader profiler to see how long a specific part took; default=false */ | ||||
| 	static const bool IS_PROFILING_ENABLED; | ||||
|  | ||||
| private: | ||||
| 	/** | ||||
| 	 * Initializes the map object from parsing the input buffer. | ||||
| @@ -90,6 +89,11 @@ private: | ||||
| 	 */ | ||||
| 	void readTeamInfo(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads the list of map flags. | ||||
| 	 */ | ||||
| 	void readMapOptions(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads the list of allowed heroes. | ||||
| 	 */ | ||||
| @@ -151,6 +155,36 @@ private: | ||||
| 	 */ | ||||
| 	void readObjects(); | ||||
|  | ||||
| 	/// Reads single object from input stream based on template | ||||
| 	CGObjectInstance * readObject(std::shared_ptr<const ObjectTemplate> objectTemplate, const int3 & objectPosition, const ObjectInstanceID & idToBeGiven); | ||||
|  | ||||
| 	CGObjectInstance * readEvent(const int3 & objectPosition); | ||||
| 	CGObjectInstance * readMonster(const int3 & objectPosition, const ObjectInstanceID & idToBeGiven); | ||||
| 	CGObjectInstance * readHero(const int3 & initialPos, const ObjectInstanceID & idToBeGiven); | ||||
| 	CGObjectInstance * readSeerHut(const int3 & initialPos); | ||||
| 	CGObjectInstance * readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); | ||||
| 	CGObjectInstance * readSign(const int3 & position); | ||||
| 	CGObjectInstance * readWitchHut(); | ||||
| 	CGObjectInstance * readScholar(); | ||||
| 	CGObjectInstance * readGarrison(const int3 & mapPosition); | ||||
| 	CGObjectInstance * readArtifact(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); | ||||
| 	CGObjectInstance * readResource(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); | ||||
| 	CGObjectInstance * readMine(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); | ||||
| 	CGObjectInstance * readPandora(const int3 & position); | ||||
| 	CGObjectInstance * readDwelling(const int3 & position); | ||||
| 	CGObjectInstance * readDwellingRandom(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); | ||||
| 	CGObjectInstance * readShrine(); | ||||
| 	CGObjectInstance * readHeroPlaceholder(const int3 & position); | ||||
| 	CGObjectInstance * readGrail(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate); | ||||
| 	CGObjectInstance * readPyramid(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); | ||||
| 	CGObjectInstance * readBorderGuard(); | ||||
| 	CGObjectInstance * readBorderGate(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); | ||||
| 	CGObjectInstance * readQuestGuard(const int3 & position); | ||||
| 	CGObjectInstance * readShipyard(const int3 & mapPosition); | ||||
| 	CGObjectInstance * readLighthouse(const int3 & mapPosition); | ||||
| 	CGObjectInstance * readGeneric(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate); | ||||
| 	CGObjectInstance * readBank(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads a creature set. | ||||
| 	 * | ||||
| @@ -160,19 +194,11 @@ private: | ||||
| 	void readCreatureSet(CCreatureSet * out, int number); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads a hero. | ||||
| 	 * Reads a quest for the given quest guard. | ||||
| 	 * | ||||
| 	 * @param idToBeGiven the object id which should be set for the hero | ||||
| 	 * @return a object instance | ||||
| 	 * @param guard the quest guard where that quest should be applied to | ||||
| 	 */ | ||||
| 	CGObjectInstance * readHero(const ObjectInstanceID & idToBeGiven, const int3 & initialPos); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads a seer hut. | ||||
| 	 * | ||||
| 	 * @return the initialized seer hut object | ||||
| 	 */ | ||||
| 	CGSeerHut * readSeerHut(const int3 & position); | ||||
| 	void readBoxContent(CGPandoraBox * object, const int3 & position); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads a quest for the given quest guard. | ||||
| @@ -181,13 +207,7 @@ private: | ||||
| 	 */ | ||||
| 	void readQuest(IQuestObject * guard, const int3 & position); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads a town. | ||||
| 	 * | ||||
| 	 * @param castleID the id of the castle type | ||||
| 	 * @return the loaded town object | ||||
| 	 */ | ||||
| 	CGTownInstance * readTown(int castleID, const int3 & position); | ||||
| 	void readSeerHutQuest(CGSeerHut * hut, const int3 & position); | ||||
|  | ||||
| 	/** | ||||
| 	 * Converts buildings to the specified castle id. | ||||
| @@ -209,42 +229,19 @@ private: | ||||
| 	*/ | ||||
| 	void readMessageAndGuards(std::string & message, CCreatureSet * guards, const int3 & position); | ||||
|  | ||||
| 	void readSpells(std::set<SpellID> & dest); | ||||
|  | ||||
| 	void readResourses(TResources& resources); | ||||
|  | ||||
| 	template <class Indenifier> | ||||
| 	void readBitmask(std::set<Indenifier> &dest, const int byteCount, const int limit, bool negate = true); | ||||
|  | ||||
| 	/** Reads bitmask to boolean vector | ||||
| 	* @param dest destination vector, shall be filed with "true" values | ||||
| 	* @param byteCount size in bytes of bimask | ||||
| 	* @param limit max count of vector elements to alter | ||||
| 	* @param negate if true then set bit in mask means clear flag in vertor | ||||
| 	*/ | ||||
| 	void readBitmask(std::vector<bool> & dest, const int byteCount, const int limit, bool negate = true); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reverses the input argument. | ||||
| 	 * | ||||
| 	 * @param arg the input argument | ||||
| 	 * @return the reversed 8-bit integer | ||||
| 	 */ | ||||
| 	ui8 reverse(ui8 arg) const; | ||||
|  | ||||
| 	/** | ||||
| 	* Helper to read map position | ||||
| 	*/ | ||||
| 	int3 readInt3(); | ||||
|  | ||||
| 	/// reads string from input stream and converts it to unicode | ||||
| 	std::string readBasicString(); | ||||
|  | ||||
| 	/// reads string from input stream, converts it to unicode and attempts to translate it | ||||
| 	std::string readLocalizedString(const TextIdentifier & identifier); | ||||
|  | ||||
| 	void setOwnerAndValidate(const int3 & mapPosition, CGObjectInstance * object, const PlayerColor & owner); | ||||
| 	void readSpells(std::set<SpellID> & dest); | ||||
|  | ||||
| 	void afterRead(); | ||||
|  | ||||
| 	MapFormatFeaturesH3M features; | ||||
|  | ||||
| 	/** List of templates loaded from the map, used on later stage to create | ||||
| 	 *  objects but not needed for fully functional CMap */ | ||||
| 	std::vector<std::shared_ptr<const ObjectTemplate>> templates; | ||||
| @@ -257,7 +254,7 @@ private: | ||||
| 	 * (when loading a map then the mapHeader ptr points to the same object) | ||||
| 	 */ | ||||
| 	std::unique_ptr<CMapHeader> mapHeader; | ||||
| 	std::unique_ptr<CBinaryReader> reader; | ||||
| 	std::unique_ptr<MapReaderH3M> reader; | ||||
| 	CInputStream * inputStream; | ||||
|  | ||||
| 	std::string mapName; | ||||
|   | ||||
							
								
								
									
										254
									
								
								lib/mapping/MapReaderH3M.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								lib/mapping/MapReaderH3M.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,254 @@ | ||||
| /* | ||||
|  * MapReaderH3M.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 "MapReaderH3M.h" | ||||
|  | ||||
| #include "../filesystem/CBinaryReader.h" | ||||
| #include "CMap.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| MapReaderH3M::MapReaderH3M(CInputStream * stream) | ||||
| 	: reader(std::make_unique<CBinaryReader>(stream)) | ||||
| { | ||||
| } | ||||
|  | ||||
| void MapReaderH3M::setFormatLevel(EMapFormat newFormat, uint8_t hotaVersion) | ||||
| { | ||||
| 	features = MapFormatFeaturesH3M::find(newFormat, hotaVersion); | ||||
| } | ||||
|  | ||||
| ArtifactID MapReaderH3M::readArtifact() | ||||
| { | ||||
| 	ArtifactID result; | ||||
|  | ||||
| 	if(features.levelAB) | ||||
| 		result = ArtifactID(reader->readUInt16()); | ||||
| 	else | ||||
| 		result = ArtifactID(reader->readUInt8()); | ||||
|  | ||||
| 	if(result == features.artifactIdentifierInvalid) | ||||
| 		return ArtifactID::NONE; | ||||
|  | ||||
| 	assert(result < features.artifactsCount); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| ArtifactID MapReaderH3M::readArtifact32() | ||||
| { | ||||
| 	ArtifactID result(reader->readInt32()); | ||||
|  | ||||
| 	if(result == ArtifactID::NONE) | ||||
| 		return ArtifactID::NONE; | ||||
|  | ||||
| 	assert(result < features.artifactsCount); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| HeroTypeID MapReaderH3M::readHero() | ||||
| { | ||||
| 	HeroTypeID result(reader->readUInt8()); | ||||
|  | ||||
| 	if(result.getNum() == features.heroIdentifierInvalid) | ||||
| 		return HeroTypeID(-1); | ||||
|  | ||||
| 	assert(result.getNum() < features.heroesPortraitsCount); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| CreatureID MapReaderH3M::readCreature() | ||||
| { | ||||
| 	CreatureID result; | ||||
|  | ||||
| 	if(features.levelAB) | ||||
| 		result = CreatureID(reader->readUInt16()); | ||||
| 	else | ||||
| 		result = CreatureID(reader->readUInt8()); | ||||
|  | ||||
| 	if(result == features.creatureIdentifierInvalid) | ||||
| 		return CreatureID::NONE; | ||||
|  | ||||
| 	if(result > features.creaturesCount) | ||||
| 	{ | ||||
| 		// this may be random creature in army/town, to be randomized later | ||||
| 		CreatureID randomIndex(result.getNum() - features.creatureIdentifierInvalid - 1); | ||||
| 		assert(randomIndex < CreatureID::NONE); | ||||
| 		assert(randomIndex > -16); | ||||
| 		return randomIndex; | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| TerrainId MapReaderH3M::readTerrain() | ||||
| { | ||||
| 	TerrainId result(readUInt8()); | ||||
| 	assert(result.getNum() < features.terrainsCount); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| RoadId MapReaderH3M::readRoad() | ||||
| { | ||||
| 	RoadId result(readInt8()); | ||||
| 	assert(result < Road::ORIGINAL_ROAD_COUNT); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| RiverId MapReaderH3M::readRiver() | ||||
| { | ||||
| 	RiverId result(readInt8()); | ||||
| 	assert(result < River::ORIGINAL_RIVER_COUNT); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| SecondarySkill MapReaderH3M::readSkill() | ||||
| { | ||||
| 	SecondarySkill result(readUInt8()); | ||||
| 	assert(result < features.skillsCount); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| SpellID MapReaderH3M::readSpell() | ||||
| { | ||||
| 	SpellID result(readUInt8()); | ||||
| 	if(result == features.spellIdentifierInvalid) | ||||
| 		return SpellID::NONE; | ||||
| 	if(result == features.spellIdentifierInvalid - 1) | ||||
| 		return SpellID::PRESET; | ||||
|  | ||||
| 	assert(result < features.spellsCount); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| SpellID MapReaderH3M::readSpell32() | ||||
| { | ||||
| 	SpellID result(readInt32()); | ||||
| 	if(result == features.spellIdentifierInvalid) | ||||
| 		return SpellID::NONE; | ||||
| 	assert(result < features.spellsCount); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| PlayerColor MapReaderH3M::readPlayer() | ||||
| { | ||||
| 	PlayerColor result(readUInt8()); | ||||
| 	assert(result < PlayerColor::PLAYER_LIMIT || result == PlayerColor::NEUTRAL); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| PlayerColor MapReaderH3M::readPlayer32() | ||||
| { | ||||
| 	PlayerColor result(readInt32()); | ||||
|  | ||||
| 	assert(result < PlayerColor::PLAYER_LIMIT || result == PlayerColor::NEUTRAL); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void MapReaderH3M::readBitmask(std::vector<bool> & dest, const int bytesToRead, const int objectsToRead, bool invert) | ||||
| { | ||||
| 	for(int byte = 0; byte < bytesToRead; ++byte) | ||||
| 	{ | ||||
| 		const ui8 mask = reader->readUInt8(); | ||||
| 		for(int bit = 0; bit < 8; ++bit) | ||||
| 		{ | ||||
| 			if(byte * 8 + bit < objectsToRead) | ||||
| 			{ | ||||
| 				const size_t index = byte * 8 + bit; | ||||
| 				const bool flag = mask & (1 << bit); | ||||
| 				const bool result = (flag != invert); | ||||
| 				dest[index] = result; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int3 MapReaderH3M::readInt3() | ||||
| { | ||||
| 	int3 p; | ||||
| 	p.x = reader->readUInt8(); | ||||
| 	p.y = reader->readUInt8(); | ||||
| 	p.z = reader->readUInt8(); | ||||
| 	return p; | ||||
| } | ||||
|  | ||||
| void MapReaderH3M::skipUnused(size_t amount) | ||||
| { | ||||
| 	reader->skip(amount); | ||||
| } | ||||
|  | ||||
| void MapReaderH3M::skipZero(size_t amount) | ||||
| { | ||||
| #ifdef NDEBUG | ||||
| 	skipUnused(amount); | ||||
| #else | ||||
| 	for(size_t i = 0; i < amount; ++i) | ||||
| 	{ | ||||
| 		uint8_t value = reader->readUInt8(); | ||||
| 		assert(value == 0); | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void MapReaderH3M::readResourses(TResources & resources) | ||||
| { | ||||
| 	for(int x = 0; x < features.resourcesCount; ++x) | ||||
| 		resources[x] = reader->readInt32(); | ||||
| } | ||||
|  | ||||
| bool MapReaderH3M::readBool() | ||||
| { | ||||
| 	uint8_t result = readUInt8(); | ||||
| 	assert(result == 0 || result == 1); | ||||
|  | ||||
| 	return result != 0; | ||||
| } | ||||
|  | ||||
| ui8 MapReaderH3M::readUInt8() | ||||
| { | ||||
| 	return reader->readUInt8(); | ||||
| } | ||||
|  | ||||
| si8 MapReaderH3M::readInt8() | ||||
| { | ||||
| 	return reader->readInt8(); | ||||
| } | ||||
|  | ||||
| ui16 MapReaderH3M::readUInt16() | ||||
| { | ||||
| 	return reader->readUInt16(); | ||||
| } | ||||
|  | ||||
| si16 MapReaderH3M::readInt16() | ||||
| { | ||||
| 	return reader->readInt16(); | ||||
| } | ||||
|  | ||||
| ui32 MapReaderH3M::readUInt32() | ||||
| { | ||||
| 	return reader->readUInt32(); | ||||
| } | ||||
|  | ||||
| si32 MapReaderH3M::readInt32() | ||||
| { | ||||
| 	return reader->readInt32(); | ||||
| } | ||||
|  | ||||
| std::string MapReaderH3M::readBaseString() | ||||
| { | ||||
| 	return reader->readBaseString(); | ||||
| } | ||||
|  | ||||
| CBinaryReader & MapReaderH3M::getInternalReader() | ||||
| { | ||||
| 	return *reader; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
							
								
								
									
										94
									
								
								lib/mapping/MapReaderH3M.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								lib/mapping/MapReaderH3M.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /* | ||||
|  * MapReaderH3M.h, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../GameConstants.h" | ||||
| #include "../ResourceSet.h" | ||||
| #include "MapFeaturesH3M.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CBinaryReader; | ||||
| class CInputStream; | ||||
| struct MapFormatFeaturesH3M; | ||||
| class int3; | ||||
| enum class EMapFormat : uint8_t; | ||||
|  | ||||
| class MapReaderH3M | ||||
| { | ||||
| public: | ||||
| 	explicit MapReaderH3M(CInputStream * stream); | ||||
|  | ||||
| 	void setFormatLevel(EMapFormat format, uint8_t hotaVersion); | ||||
|  | ||||
| 	ArtifactID readArtifact(); | ||||
| 	ArtifactID readArtifact32(); | ||||
| 	CreatureID readCreature(); | ||||
| 	HeroTypeID readHero(); | ||||
| 	TerrainId readTerrain(); | ||||
| 	RoadId readRoad(); | ||||
| 	RiverId readRiver(); | ||||
| 	SecondarySkill readSkill(); | ||||
| 	SpellID readSpell(); | ||||
| 	SpellID readSpell32(); | ||||
| 	PlayerColor readPlayer(); | ||||
| 	PlayerColor readPlayer32(); | ||||
|  | ||||
| 	template<class Identifier> | ||||
| 	void readBitmask(std::set<Identifier> & dest, int bytesToRead, int objectsToRead, bool invert) | ||||
| 	{ | ||||
| 		std::vector<bool> bitmap; | ||||
| 		bitmap.resize(objectsToRead, false); | ||||
| 		readBitmask(bitmap, bytesToRead, objectsToRead, invert); | ||||
|  | ||||
| 		for(int i = 0; i < bitmap.size(); i++) | ||||
| 			if(bitmap[i]) | ||||
| 				dest.insert(static_cast<Identifier>(i)); | ||||
| 	} | ||||
|  | ||||
| 	/** Reads bitmask to boolean vector | ||||
| 	* @param dest destination vector, shall be filed with "true" values | ||||
| 	* @param byteCount size in bytes of bimask | ||||
| 	* @param limit max count of vector elements to alter | ||||
| 	* @param negate if true then set bit in mask means clear flag in vertor | ||||
| 	*/ | ||||
| 	void readBitmask(std::vector<bool> & dest, int bytesToRead, int objectsToRead, bool invert); | ||||
|  | ||||
| 	/** | ||||
| 	* Helper to read map position | ||||
| 	*/ | ||||
| 	int3 readInt3(); | ||||
|  | ||||
| 	void skipUnused(size_t amount); | ||||
| 	void skipZero(size_t amount); | ||||
|  | ||||
| 	void readResourses(TResources & resources); | ||||
|  | ||||
| 	bool readBool(); | ||||
|  | ||||
| 	ui8 readUInt8(); | ||||
| 	si8 readInt8(); | ||||
| 	ui16 readUInt16(); | ||||
| 	si16 readInt16(); | ||||
| 	ui32 readUInt32(); | ||||
| 	si32 readInt32(); | ||||
|  | ||||
| 	std::string readBaseString(); | ||||
|  | ||||
| 	CBinaryReader & getInternalReader(); | ||||
|  | ||||
| private: | ||||
| 	MapFormatFeaturesH3M features; | ||||
|  | ||||
| 	std::unique_ptr<CBinaryReader> reader; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| @@ -118,7 +118,8 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase | ||||
| 	{ | ||||
| 		ui32 length; | ||||
| 		load(length); | ||||
| 		if(length > 500000) | ||||
| 		//NOTE: also used for h3m's embedded in campaigns, so it may be quite large in some cases (e.g. XXL maps with multiple objects) | ||||
| 		if(length > 1000000) | ||||
| 		{ | ||||
| 			logGlobal->warn("Warning: very big length: %d", length); | ||||
| 			reader->reportState(logGlobal); | ||||
|   | ||||
| @@ -67,7 +67,7 @@ bool Summon::applicable(Problem & problem, const Mechanics * m) const | ||||
|  | ||||
| 				text.addReplacement(MetaString::CRE_PL_NAMES, elemental->creatureIndex()); | ||||
|  | ||||
| 				if(caster->type->sex) | ||||
| 				if(caster->type->gender == EHeroGender::FEMALE) | ||||
| 					text.addReplacement(MetaString::GENERAL_TXT, 540); | ||||
| 				else | ||||
| 					text.addReplacement(MetaString::GENERAL_TXT, 539); | ||||
|   | ||||
| @@ -68,7 +68,7 @@ void ArmyWidget::obtainData() | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(army.formation) | ||||
| 	if(army.formation == EArmyFormation::TIGHT) | ||||
| 		ui->formationTight->setChecked(true); | ||||
| 	else | ||||
| 		ui->formationWide->setChecked(true); | ||||
|   | ||||
| @@ -132,7 +132,7 @@ void Initializer::initialize(CGHeroInstance * o) | ||||
| 	if(!o->type) | ||||
| 		o->type = VLC->heroh->objects.at(o->subID); | ||||
| 	 | ||||
| 	o->sex = o->type->sex; | ||||
| 	o->gender = o->type->gender; | ||||
| 	o->portrait = o->type->imageIndex; | ||||
| 	o->randomizeArmy(o->type->heroClass->faction); | ||||
| } | ||||
| @@ -241,7 +241,7 @@ void Inspector::updateProperties(CGHeroInstance * o) | ||||
| 	{ //Sex | ||||
| 		auto * delegate = new InspectorDelegate; | ||||
| 		delegate->options << "MALE" << "FEMALE"; | ||||
| 		addProperty<std::string>("Sex", (o->sex ? "FEMALE" : "MALE"), delegate , false); | ||||
| 		addProperty<std::string>("Gender", (o->gender == EHeroGender::FEMALE ? "FEMALE" : "MALE"), delegate , false); | ||||
| 	} | ||||
| 	addProperty("Name", o->nameCustom, false); | ||||
| 	addProperty("Biography", o->biographyCustom, new MessageDelegate, false); | ||||
| @@ -549,8 +549,8 @@ void Inspector::setProperty(CGHeroInstance * o, const QString & key, const QVari | ||||
| { | ||||
| 	if(!o) return; | ||||
| 	 | ||||
| 	if(key == "Sex") | ||||
| 		o->sex = value.toString() == "MALE" ? 0 : 1; | ||||
| 	if(key == "Gender") | ||||
| 		o->gender = value.toString() == "MALE" ? EHeroGender::MALE : EHeroGender::FEMALE; | ||||
| 	 | ||||
| 	if(key == "Name") | ||||
| 		o->nameCustom = value.toString().toStdString(); | ||||
| @@ -565,7 +565,7 @@ void Inspector::setProperty(CGHeroInstance * o, const QString & key, const QVari | ||||
| 			if(t->getNameTranslated() == value.toString().toStdString()) | ||||
| 				o->type = t.get(); | ||||
| 		} | ||||
| 		o->sex = o->type->sex; | ||||
| 		o->gender = o->type->gender; | ||||
| 		o->portrait = o->type->imageIndex; | ||||
| 		o->randomizeArmy(o->type->heroClass->faction); | ||||
| 		updateProperties(); //updating other properties after change | ||||
|   | ||||
		Reference in New Issue
	
	Block a user