Browse Source

Refactored loadObject() monstrocity into set of smaller methods.

Ivan Savenko 2 years ago
parent
commit
3790661fa6
2 changed files with 439 additions and 397 deletions
  1. 406 378
      lib/mapping/MapFormatH3M.cpp
  2. 33 19
      lib/mapping/MapFormatH3M.h

+ 406 - 378
lib/mapping/MapFormatH3M.cpp

@@ -936,81 +936,366 @@ void CMapLoaderH3M::readDefInfo()
 	}
 }
 
-void CMapLoaderH3M::readObjects()
+CGObjectInstance * CMapLoaderH3M::readEvent(const int3 & objPos)
 {
-	uint32_t howManyObjs = reader->readUInt32();
+	auto * object = new CGEvent();
 
-	for(uint32_t ww = 0; ww < howManyObjs; ++ww)
+	readBoxContent(object, objPos);
+
+	object->availableFor = reader->readUInt8();
+	object->computerActivate = reader->readBool();
+	object->removeAfterVisit = reader->readBool();
+	object->humanActivate = true;
+
+	reader->skipZero(4);
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readPandora(const int3 & position)
+{
+	auto * object = new CGPandoraBox();
+	readBoxContent(object, position);
+	return object;
+}
+
+void CMapLoaderH3M::readBoxContent(CGPandoraBox * object, const int3 & position)
+{
+	readMessageAndGuards(object->message, object, position);
+
+	object->gainedExp = reader->readUInt32();
+	object->manaDiff = reader->readInt32();
+	object->moraleDiff = reader->readInt8();
+	object->luckDiff = reader->readInt8();
+
+	reader->readResourses(object->resources);
+
+	object->primskills.resize(GameConstants::PRIMARY_SKILLS);
+	for(int x = 0; x < GameConstants::PRIMARY_SKILLS; ++x)
+		object->primskills[x] = static_cast<PrimarySkill::PrimarySkill>(reader->readUInt8());
+
+	int gabn = reader->readUInt8();//number of gained abilities
+	for(int oo = 0; oo < gabn; ++oo)
 	{
-		CGObjectInstance * nobj = nullptr;
+		object->abilities.emplace_back(reader->readSkill());
+		object->abilityLevels.push_back(reader->readUInt8());
+	}
+	int gart = reader->readUInt8(); //number of gained artifacts
+	for(int oo = 0; oo < gart; ++oo)
+		object->artifacts.emplace_back(reader->readArtifact());
 
-		int3 objPos = reader->readInt3();
+	int gspel = reader->readUInt8(); //number of gained spells
+	for(int oo = 0; oo < gspel; ++oo)
+		object->spells.emplace_back(reader->readSpell());
 
-		uint32_t defnum = reader->readUInt32();
-		ObjectInstanceID idToBeGiven = ObjectInstanceID(static_cast<si32>(map->objects.size()));
+	int gcre = reader->readUInt8(); //number of gained creatures
+	readCreatureSet(&object->creatures, gcre);
+	reader->skipZero(8);
+}
 
-		std::shared_ptr<const ObjectTemplate> objTempl = templates.at(defnum);
-		reader->skipZero(5);
+CGObjectInstance * CMapLoaderH3M::readMonster(const int3 & objPos, const ObjectInstanceID & idToBeGiven)
+{
+	auto * object = new CGCreature();
+
+	if(features.levelAB)
+	{
+		object->identifier = reader->readUInt32();
+		map->questIdentifierToId[object->identifier] = idToBeGiven;
+	}
+
+	auto * hlp = new CStackInstance();
+	hlp->count = reader->readUInt16();
+
+	//type will be set during initialization
+	object->putStack(SlotID(0), hlp);
+
+	object->character = reader->readInt8();
+
+	bool hasMessage = reader->readBool();
+	if(hasMessage)
+	{
+		object->message = readLocalizedString(TextIdentifier("monster", objPos.x, objPos.y, objPos.z, "message"));
+		reader->readResourses(object->resources);
+		object->gainedArtifact = reader->readArtifact();
+	}
+	object->neverFlees = reader->readBool();
+	object->notGrowingTeam = reader->readBool();
+	reader->skipZero(2);
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readSign(const int3 & objPos)
+{
+	auto * object = new CGSignBottle();
+	object->message = readLocalizedString(TextIdentifier("sign", objPos.x, objPos.y, objPos.z, "message"));
+	reader->skipZero(4);
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readWitchHut(const int3 & position)
+{
+	auto * object = new CGWitchHut();
 
-		switch(objTempl->id)
+	// AB and later maps have allowed abilities defined in H3M
+	if(features.levelAB)
+	{
+		reader->readBitmask(object->allowedAbilities, features.skillsBytes, features.skillsCount, false);
+
+		if(object->allowedAbilities.size() != 1)
 		{
-		case Obj::EVENT:
-			{
-				auto * evnt = new CGEvent();
-				nobj = evnt;
+			auto defaultAllowed = VLC->skillh->getDefaultAllowed();
+
+			for(int skillID = 0; skillID < VLC->skillh->size(); ++skillID)
+				if (defaultAllowed[skillID])
+					object->allowedAbilities.insert(skillID);
+		}
+	}
+	return object;
+}
 
-				readMessageAndGuards(evnt->message, evnt, objPos);
+CGObjectInstance * CMapLoaderH3M::readScholar(const int3 & position)
+{
+	auto * object = new CGScholar();
+	object->bonusType = static_cast<CGScholar::EBonusType>(reader->readUInt8());
+	object->bonusID = reader->readUInt8();
+	reader->skipZero(6);
+	return object;
+}
 
-				evnt->gainedExp = reader->readUInt32();
-				evnt->manaDiff = reader->readInt32();
-				evnt->moraleDiff = reader->readInt8();
-				evnt->luckDiff = reader->readInt8();
+CGObjectInstance * CMapLoaderH3M::readGarrison(const int3 & position)
+{
+	auto * object = new CGGarrison();
 
-				reader->readResourses(evnt->resources);
+	object->setOwner(reader->readPlayer32());
+	readCreatureSet(object, 7);
+	if(features.levelAB)
+		object->removableUnits = reader->readBool();
+	else
+		object->removableUnits = true;
 
-				evnt->primskills.resize(GameConstants::PRIMARY_SKILLS);
-				for(int x = 0; x < 4; ++x)
-				{
-					evnt->primskills[x] = static_cast<PrimarySkill::PrimarySkill>(reader->readUInt8());
-				}
+	reader->skipZero(8);
+	return object;
+}
 
-				int gabn = reader->readUInt8(); // Number of gained abilities
-				for(int oo = 0; oo < gabn; ++oo)
-				{
-					evnt->abilities.emplace_back(reader->readSkill());
-					evnt->abilityLevels.push_back(reader->readUInt8());
-				}
+CGObjectInstance * CMapLoaderH3M::readArtifact(const int3 & objPos, std::shared_ptr<const ObjectTemplate> objTempl)
+{
+	auto artID = ArtifactID::NONE; //random, set later
+	int spellID = -1;
+	auto * object = new CGArtifact();
 
-				int gart = reader->readUInt8(); // Number of gained artifacts
-				for(int oo = 0; oo < gart; ++oo)
-					evnt->artifacts.emplace_back(reader->readArtifact());
+	readMessageAndGuards(object->message, object, objPos);
 
-				int gspel = reader->readUInt8(); // Number of gained spells
-				for(int oo = 0; oo < gspel; ++oo)
-				{
-					evnt->spells.emplace_back(reader->readSpell());
-				}
+	if(objTempl->id == Obj::SPELL_SCROLL)
+	{
+		spellID = reader->readSpell32();
+		artID = ArtifactID::SPELL_SCROLL;
+	}
+	else if(objTempl->id == Obj::ARTIFACT)
+	{
+		//specific artifact
+		artID = ArtifactID(objTempl->subid);
+	}
 
-				int gcre = reader->readUInt8(); //number of gained creatures
-				readCreatureSet(&evnt->creatures, gcre);
+	object->storedArtifact = CArtifactInstance::createArtifact(map, artID, spellID);
+	return object;
+}
 
-				reader->skipZero(8);
-				evnt->availableFor = reader->readUInt8();
-				evnt->computerActivate = reader->readBool();
-				evnt->removeAfterVisit = reader->readBool();
-				evnt->humanActivate = true;
+CGObjectInstance * CMapLoaderH3M::readResource(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl)
+{
+	auto * object = new CGResource();
+
+	readMessageAndGuards(object->message, object, position);
+
+	object->amount = reader->readUInt32();
+	if(objTempl->subid == GameResID(EGameResID::GOLD))
+	{
+		// Gold is multiplied by 100.
+		object->amount *= 100;
+	}
+	reader->skipZero(4);
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readMine(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl)
+{
+	auto * object = new CGMine();
+	if (objTempl->subid < 7 )
+	{
+		object->setOwner(reader->readPlayer32());
+	}
+	else
+	{
+		object->setOwner(PlayerColor::NEUTRAL);
+		reader->readBitmask(object->abandonedMineResources, features.resourcesBytes, features.resourcesCount, false);
+	}
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readDwelling(const int3 & position)
+{
+	auto * object = new CGDwelling();
+	object->setOwner(reader->readPlayer32());
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readDwellingRandom(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl )
+{
+	auto * object = new CGDwelling();
+
+	CSpecObjInfo * spec = nullptr;
+	switch(objTempl->id)
+	{
+	case Obj::RANDOM_DWELLING:
+		spec = new CCreGenLeveledCastleInfo();
+		break;
+	case Obj::RANDOM_DWELLING_LVL:
+		spec = new CCreGenAsCastleInfo();
+		break;
+	case Obj::RANDOM_DWELLING_FACTION:
+		spec = new CCreGenLeveledInfo();
+		break;
+	default:
+		throw std::runtime_error("Invalid random dwelling format");
+	}
+	spec->owner = object;
+
+	object->setOwner(reader->readPlayer32());
+
+	//216 and 217
+	if(auto * castleSpec = dynamic_cast<CCreGenAsCastleInfo *>(spec))
+	{
+		castleSpec->instanceId = "";
+		castleSpec->identifier = reader->readUInt32();
+		if(!castleSpec->identifier)
+		{
+			castleSpec->asCastle = false;
+			const int MASK_SIZE = 8;
+			ui8 mask[2];
+			mask[0] = reader->readUInt8();
+			mask[1] = reader->readUInt8();
+
+			castleSpec->allowedFactions.clear();
+			castleSpec->allowedFactions.resize(VLC->townh->size(), false);
+
+			for(int i = 0; i < MASK_SIZE; i++)
+				castleSpec->allowedFactions[i] = ((mask[0] & (1 << i))>0);
+
+			for(int i = 0; i < (GameConstants::F_NUMBER-MASK_SIZE); i++)
+				castleSpec->allowedFactions[i+MASK_SIZE] = ((mask[1] & (1 << i))>0);
+		}
+		else
+		{
+			castleSpec->asCastle = true;
+		}
+	}
+
+	//216 and 218
+	if(auto * lvlSpec = dynamic_cast<CCreGenLeveledInfo *>(spec))
+	{
+		lvlSpec->minLevel = std::max(reader->readUInt8(), static_cast<ui8>(1));
+		lvlSpec->maxLevel = std::min(reader->readUInt8(), static_cast<ui8>(7));
+	}
+	object->info = spec;
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readShrine(const int3 & position)
+{
+	auto * object = new CGShrine();
+	object->spell = reader->readSpell32();
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readHeroPlaceholder(const int3 & position)
+{
+	auto * object = new CGHeroPlaceholder();
+
+	object->setOwner(reader->readPlayer());
+
+	HeroTypeID htid = reader->readHero(); //hero type id
+	object->subID = htid.getNum();
+
+	if(htid.getNum() == -1)
+	{
+		object->power = reader->readUInt8();
+		logGlobal->debug("Hero placeholder: by power at %s", position.toString());
+	}
+	else
+	{
+		object->power = 0;
+		logGlobal->debug("Hero placeholder: %s at %s", VLC->heroh->getById(htid)->getNameTranslated(), position.toString());
+	}
+
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readGrail(const int3 & position)
+{
+	map->grailPos = position;
+	map->grailRadius = reader->readInt32();
+	return nullptr;
+}
+
+CGObjectInstance * CMapLoaderH3M::readBlank(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl)
+{
+	if (VLC->objtypeh->knownSubObjects(objTempl->id).count(objTempl->subid))
+		return  VLC->objtypeh->getHandlerFor(objTempl->id, objTempl->subid)->create(objTempl);
+
+	logGlobal->warn("Unrecognized object: %d:%d ('%s') at %s on map '%s'", objTempl->id.toEnum(), objTempl->subid, objTempl->animationFile, position.toString(), map->name);
+	return new CGObjectInstance();
+}
+
+CGObjectInstance * CMapLoaderH3M::readPyramid(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl)
+{
+	if(objTempl->subid == 0)
+		return new CBank();
+
+	return new CGObjectInstance();
+}
+
+CGObjectInstance * CMapLoaderH3M::readQuestGuard(const int3 & position)
+{
+	auto * guard = new CGQuestGuard();
+	readQuest(guard, position);
+	return guard;
+}
+
+CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & position)
+{
+	auto * object = new CGShipyard();
+	object->setOwner(reader->readPlayer32());
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readBorderGuard(const int3 & position)
+{
+	return new CGBorderGuard();
+}
+
+CGObjectInstance * CMapLoaderH3M::readBorderGate(const int3 & position)
+{
+	return new CGBorderGate();
+}
+
+CGObjectInstance * CMapLoaderH3M::readLighthouse(const int3 & position)
+{
+	auto * object = new CGLighthouse();
+	object->tempOwner = reader->readPlayer32();
+	return object;
+}
+
+CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr<const ObjectTemplate> objectTemplate, const int3 & objectPosition, const ObjectInstanceID & idToBeGiven)
+{
+	switch(objectTemplate->id)
+	{
+		case Obj::EVENT:
+			return readEvent(objectPosition);
 
-				reader->skipZero(4);
-				break;
-			}
 		case Obj::HERO:
 		case Obj::RANDOM_HERO:
 		case Obj::PRISON:
-			{
-				nobj = readHero(idToBeGiven, objPos);
-				break;
-			}
-		case Obj::MONSTER:  //Monster
+			return readHero(objectPosition, idToBeGiven);
+
+		case Obj::MONSTER:
 		case Obj::RANDOM_MONSTER:
 		case Obj::RANDOM_MONSTER_L1:
 		case Obj::RANDOM_MONSTER_L2:
@@ -1019,95 +1304,24 @@ void CMapLoaderH3M::readObjects()
 		case Obj::RANDOM_MONSTER_L5:
 		case Obj::RANDOM_MONSTER_L6:
 		case Obj::RANDOM_MONSTER_L7:
-			{
-				auto * cre = new CGCreature();
-				nobj = cre;
-
-				if(features.levelAB)
-				{
-					cre->identifier = reader->readUInt32();
-					map->questIdentifierToId[cre->identifier] = idToBeGiven;
-				}
+			return readMonster(objectPosition, idToBeGiven);
 
-				auto * hlp = new CStackInstance();
-				hlp->count = reader->readUInt16();
-
-				//type will be set during initialization
-				cre->putStack(SlotID(0), hlp);
-
-				cre->character = reader->readInt8();
-
-				bool hasMessage = reader->readBool();
-				if(hasMessage)
-				{
-					cre->message = readLocalizedString(TextIdentifier("monster", objPos.x, objPos.y, objPos.z, "message"));
-					reader->readResourses(cre->resources);
-					cre->gainedArtifact = reader->readArtifact();
-				}
-				cre->neverFlees = reader->readBool();
-				cre->notGrowingTeam = reader->readBool();
-				reader->skipZero(2);
-				break;
-			}
 		case Obj::OCEAN_BOTTLE:
 		case Obj::SIGN:
-			{
-				auto * sb = new CGSignBottle();
-				nobj = sb;
-				sb->message = readLocalizedString(TextIdentifier("sign", objPos.x, objPos.y, objPos.z, "message"));
-				reader->skipZero(4);
-				break;
-			}
-		case Obj::SEER_HUT:
-			{
-				nobj = readSeerHut(objPos);
-				break;
-			}
-		case Obj::WITCH_HUT:
-			{
-				auto * wh = new CGWitchHut();
-				nobj = wh;
-
-				// AB and later maps have allowed abilities defined in H3M
-				if(features.levelAB)
-				{
-					reader->readBitmask(wh->allowedAbilities, features.skillsBytes, features.skillsCount, false);
+			return readSign(objectPosition);
 
-					if(wh->allowedAbilities.size() != 1)
-					{
-						auto defaultAllowed = VLC->skillh->getDefaultAllowed();
+		case Obj::SEER_HUT:
+			return readSeerHut(objectPosition);
 
-						for(int skillID = 0; skillID < VLC->skillh->size(); ++skillID)
-							if (defaultAllowed[skillID])
-								wh->allowedAbilities.insert(skillID);
-					}
-				}
-				break;
-			}
+		case Obj::WITCH_HUT:
+			return readWitchHut(objectPosition);
 		case Obj::SCHOLAR:
-			{
-				auto * sch = new CGScholar();
-				nobj = sch;
-				sch->bonusType = static_cast<CGScholar::EBonusType>(reader->readUInt8());
-				sch->bonusID = reader->readUInt8();
-				reader->skipZero(6);
-				break;
-			}
+			return readScholar(objectPosition);
+
 		case Obj::GARRISON:
 		case Obj::GARRISON2:
-			{
-				auto * gar = new CGGarrison();
-				nobj = gar;
-				nobj->setOwner(reader->readPlayer32());
-				readCreatureSet(gar, 7);
-				if(features.levelAB)
-					gar->removableUnits = reader->readBool();
-				else
-					gar->removableUnits = true;
-
-				reader->skipZero(8);
-				break;
-			}
+			return readGarrison(objectPosition);
+
 		case Obj::ARTIFACT:
 		case Obj::RANDOM_ART:
 		case Obj::RANDOM_TREASURE_ART:
@@ -1115,271 +1329,85 @@ void CMapLoaderH3M::readObjects()
 		case Obj::RANDOM_MAJOR_ART:
 		case Obj::RANDOM_RELIC_ART:
 		case Obj::SPELL_SCROLL:
-			{
-				auto artID = ArtifactID::NONE; //random, set later
-				int spellID = -1;
-				auto * art = new CGArtifact();
-				nobj = art;
-
-				readMessageAndGuards(art->message, art, objPos);
-
-				if(objTempl->id == Obj::SPELL_SCROLL)
-				{
-					spellID = reader->readSpell32();
-					artID = ArtifactID::SPELL_SCROLL;
-				}
-				else if(objTempl->id == Obj::ARTIFACT)
-				{
-					//specific artifact
-					artID = ArtifactID(objTempl->subid);
-				}
+			return readArtifact(objectPosition, objectTemplate);
 
-				art->storedArtifact = CArtifactInstance::createArtifact(map, artID, spellID);
-				break;
-			}
 		case Obj::RANDOM_RESOURCE:
 		case Obj::RESOURCE:
-			{
-				auto * res = new CGResource();
-				nobj = res;
-
-				readMessageAndGuards(res->message, res, objPos);
-
-				res->amount = reader->readUInt32();
-				if(objTempl->subid == GameResID(EGameResID::GOLD))
-				{
-					// Gold is multiplied by 100.
-					res->amount *= 100;
-				}
-				reader->skipZero(4);
-				break;
-			}
+			return readResource(objectPosition, objectTemplate);
 		case Obj::RANDOM_TOWN:
 		case Obj::TOWN:
-			{
-				nobj = readTown(objTempl->subid, objPos);
-				break;
-			}
+			return readTown(objectPosition, objectTemplate);
+
 		case Obj::MINE:
 		case Obj::ABANDONED_MINE:
-			{
-				auto * mine = new CGMine();
-				nobj = mine;
-				if (objTempl->subid < 7 )
-				{
-					mine->setOwner(reader->readPlayer32());
-				}
-				else
-				{
-					mine->setOwner(PlayerColor::NEUTRAL);
-					reader->readBitmask(mine->abandonedMineResources, features.resourcesBytes, features.resourcesCount, false);
-				}
-				break;
-			}
+			return readMine(objectPosition, objectTemplate);
+
 		case Obj::CREATURE_GENERATOR1:
 		case Obj::CREATURE_GENERATOR2:
 		case Obj::CREATURE_GENERATOR3:
 		case Obj::CREATURE_GENERATOR4:
-			{
-				nobj = new CGDwelling();
-				nobj->setOwner(reader->readPlayer32());
-				break;
-			}
+			return readDwelling(objectPosition);
+
 		case Obj::SHRINE_OF_MAGIC_INCANTATION:
 		case Obj::SHRINE_OF_MAGIC_GESTURE:
 		case Obj::SHRINE_OF_MAGIC_THOUGHT:
-			{
-				auto * shr = new CGShrine();
-				nobj = shr;
-				shr->spell = reader->readSpell32();
-				break;
-			}
+			return readShrine(objectPosition);
+
 		case Obj::PANDORAS_BOX:
-			{
-				auto * box = new CGPandoraBox();
-				nobj = box;
-				readMessageAndGuards(box->message, box, objPos);
+			return readPandora(objectPosition);
 
-				box->gainedExp = reader->readUInt32();
-				box->manaDiff = reader->readInt32();
-				box->moraleDiff = reader->readInt8();
-				box->luckDiff = reader->readInt8();
+		case Obj::GRAIL:
+			return readGrail(objectPosition);
 
-				reader->readResourses(box->resources);
+		case Obj::RANDOM_DWELLING:
+		case Obj::RANDOM_DWELLING_LVL:
+		case Obj::RANDOM_DWELLING_FACTION:
+			return readDwellingRandom(objectPosition, objectTemplate);
 
-				box->primskills.resize(GameConstants::PRIMARY_SKILLS);
-				for(int x = 0; x < GameConstants::PRIMARY_SKILLS; ++x)
-				{
-					box->primskills[x] = static_cast<PrimarySkill::PrimarySkill>(reader->readUInt8());
-				}
+		case Obj::QUEST_GUARD:
+			return readQuestGuard(objectPosition);
 
-				int gabn = reader->readUInt8();//number of gained abilities
-				for(int oo = 0; oo < gabn; ++oo)
-				{
-					box->abilities.emplace_back(reader->readSkill());
-					box->abilityLevels.push_back(reader->readUInt8());
-				}
-				int gart = reader->readUInt8(); //number of gained artifacts
-				for(int oo = 0; oo < gart; ++oo)
-					box->artifacts.emplace_back(reader->readArtifact());
+		case Obj::SHIPYARD:
+			return readShipyard(objectPosition);
 
-				int gspel = reader->readUInt8(); //number of gained spells
-				for(int oo = 0; oo < gspel; ++oo)
-					box->spells.emplace_back(reader->readSpell());
+		case Obj::HERO_PLACEHOLDER:
+			return readHeroPlaceholder(objectPosition);
 
-				int gcre = reader->readUInt8(); //number of gained creatures
-				readCreatureSet(&box->creatures, gcre);
-				reader->skipZero(8);
-				break;
-			}
-		case Obj::GRAIL:
-			{
-				map->grailPos = objPos;
-				map->grailRadius = reader->readInt32();
-				continue;
-			}
-		case Obj::RANDOM_DWELLING: //same as castle + level range
-		case Obj::RANDOM_DWELLING_LVL: //same as castle, fixed level
-		case Obj::RANDOM_DWELLING_FACTION: //level range, fixed faction
-			{
-				auto * dwelling = new CGDwelling();
-				nobj = dwelling;
-				CSpecObjInfo * spec = nullptr;
-				switch(objTempl->id)
-				{
-				case Obj::RANDOM_DWELLING:
-					spec = new CCreGenLeveledCastleInfo();
-					break;
-				case Obj::RANDOM_DWELLING_LVL:
-					spec = new CCreGenAsCastleInfo();
-					break;
-				case Obj::RANDOM_DWELLING_FACTION:
-					spec = new CCreGenLeveledInfo();
-					break;
-				default:
-					throw std::runtime_error("Invalid random dwelling format");
-				}
-				spec->owner = dwelling;
+		case Obj::BORDERGUARD:
+			return readBorderGuard(objectPosition);
 
-				nobj->setOwner(reader->readPlayer32());
+		case Obj::BORDER_GATE:
+			return readBorderGate(objectPosition);
 
-				//216 and 217
-				if(auto * castleSpec = dynamic_cast<CCreGenAsCastleInfo *>(spec))
-				{
-					castleSpec->instanceId = "";
-					castleSpec->identifier = reader->readUInt32();
-					if(!castleSpec->identifier)
-					{
-						castleSpec->asCastle = false;
-						const int MASK_SIZE = 8;
-						ui8 mask[2];
-						mask[0] = reader->readUInt8();
-						mask[1] = reader->readUInt8();
-
-						castleSpec->allowedFactions.clear();
-						castleSpec->allowedFactions.resize(VLC->townh->size(), false);
-
-						for(int i = 0; i < MASK_SIZE; i++)
-							castleSpec->allowedFactions[i] = ((mask[0] & (1 << i))>0);
-
-						for(int i = 0; i < (GameConstants::F_NUMBER-MASK_SIZE); i++)
-							castleSpec->allowedFactions[i+MASK_SIZE] = ((mask[1] & (1 << i))>0);
-					}
-					else
-					{
-						castleSpec->asCastle = true;
-					}
-				}
+		case Obj::PYRAMID:
+			return readPyramid(objectPosition, objectTemplate);
 
-				//216 and 218
-				if(auto * lvlSpec = dynamic_cast<CCreGenLeveledInfo *>(spec))
-				{
-					lvlSpec->minLevel = std::max(reader->readUInt8(), static_cast<ui8>(1));
-					lvlSpec->maxLevel = std::min(reader->readUInt8(), static_cast<ui8>(7));
-				}
-				dwelling->info = spec;
-				break;
-			}
-		case Obj::QUEST_GUARD:
-			{
-				auto * guard = new CGQuestGuard();
-				readQuest(guard, objPos);
-				nobj = guard;
-				break;
-			}
-		case Obj::SHIPYARD:
-			{
-				nobj = new CGShipyard();
-				nobj->setOwner(reader->readPlayer32());
-				break;
-			}
-		case Obj::HERO_PLACEHOLDER: //hero placeholder
-			{
-				auto * hp = new CGHeroPlaceholder();
-				nobj = hp;
+		case Obj::LIGHTHOUSE:
+			return readLighthouse(objectPosition);
 
-				hp->setOwner(reader->readPlayer());
+		default: //any other object
+			return readBlank(objectPosition, objectTemplate);
+	}
+}
 
-				HeroTypeID htid = reader->readHero(); //hero type id
-				nobj->subID = htid.getNum();
+void CMapLoaderH3M::readObjects()
+{
+	uint32_t howManyObjs = reader->readUInt32();
 
-				if(htid.getNum() == -1)
-				{
-					hp->power = reader->readUInt8();
-					logGlobal->debug("Hero placeholder: by power at %s", objPos.toString());
-				}
-				else
-				{
-					logGlobal->debug("Hero placeholder: %s at %s", VLC->heroh->getById(htid)->getNameTranslated(), objPos.toString());
-					hp->power = 0;
-				}
+	for(uint32_t ww = 0; ww < howManyObjs; ++ww)
+	{
+		int3 objPos = reader->readInt3();
 
-				break;
-			}
-		case Obj::BORDERGUARD:
-			{
-				nobj = new CGBorderGuard();
-				break;
-			}
-		case Obj::BORDER_GATE:
-			{
-				nobj = new CGBorderGate();
-				break;
-			}
-		case Obj::PYRAMID: //Pyramid of WoG object
-			{
-				if(objTempl->subid == 0)
-				{
-					nobj = new CBank();
-				}
-				else
-				{
-					//WoG object
-					//TODO: possible special handling
-					nobj = new CGObjectInstance();
-				}
-				break;
-			}
-		case Obj::LIGHTHOUSE: //Lighthouse
-			{
-				nobj = new CGLighthouse();
-				nobj->tempOwner = reader->readPlayer32();
-				break;
-			}
-		default: //any other object
-			{
-				if (VLC->objtypeh->knownSubObjects(objTempl->id).count(objTempl->subid))
-				{
-					nobj = VLC->objtypeh->getHandlerFor(objTempl->id, objTempl->subid)->create(objTempl);
-				}
-				else
-				{
-					logGlobal->warn("Unrecognized object: %d:%d ('%s') at %s on map '%s'", objTempl->id.toEnum(), objTempl->subid, objTempl->animationFile, objPos.toString(), map->name);
-					nobj = new CGObjectInstance();
-				}
-				break;
-			}
-		}
+		uint32_t defnum = reader->readUInt32();
+		ObjectInstanceID idToBeGiven = ObjectInstanceID(static_cast<si32>(map->objects.size()));
+
+		std::shared_ptr<const ObjectTemplate> objTempl = templates.at(defnum);
+		reader->skipZero(5);
+
+		CGObjectInstance * nobj = readObject(objTempl, objPos, idToBeGiven);
+
+		if (!nobj)
+			continue;
 
 		nobj->pos = objPos;
 		nobj->ID = objTempl->id;
@@ -1443,7 +1471,7 @@ void CMapLoaderH3M::readCreatureSet(CCreatureSet * out, int number)
 	out->validTypes(true);
 }
 
-CGObjectInstance * CMapLoaderH3M::readHero(const ObjectInstanceID & idToBeGiven, const int3 & initialPos)
+CGObjectInstance * CMapLoaderH3M::readHero(const int3 & initialPos, const ObjectInstanceID & idToBeGiven)
 {
 	auto * nhi = new CGHeroInstance();
 
@@ -1601,7 +1629,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const ObjectInstanceID & idToBeGiven,
 	return nhi;
 }
 
-CGSeerHut * CMapLoaderH3M::readSeerHut(const int3 & position)
+CGObjectInstance * CMapLoaderH3M::readSeerHut(const int3 & position)
 {
 	auto * hut = new CGSeerHut();
 
@@ -1791,7 +1819,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
 	guard->quest->isCustomComplete = !guard->quest->completedText.empty();
 }
 
-CGTownInstance * CMapLoaderH3M::readTown(int castleID, const int3 & position)
+CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl)
 {
 	auto * nt = new CGTownInstance();
 	if(features.levelAB)
@@ -1815,8 +1843,8 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID, const int3 & position)
 		reader->readBitmask(nt->builtBuildings, features.buildingsBytes,features.buildingsCount,false);
 		reader->readBitmask(nt->forbiddenBuildings,features.buildingsBytes,features.buildingsCount,false);
 
-		nt->builtBuildings = convertBuildings(nt->builtBuildings, castleID);
-		nt->forbiddenBuildings = convertBuildings(nt->forbiddenBuildings, castleID);
+		nt->builtBuildings = convertBuildings(nt->builtBuildings, objTempl->subid);
+		nt->forbiddenBuildings = convertBuildings(nt->forbiddenBuildings, objTempl->subid);
 	}
 	// Standard buildings
 	else
@@ -1879,7 +1907,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID, const int3 & position)
 
 		reader->readBitmask(nce.buildings, features.buildingsBytes, features.buildingsCount, false);
 
-		nce.buildings = convertBuildings(nce.buildings, castleID, false);
+		nce.buildings = convertBuildings(nce.buildings, objTempl->subid, false);
 
 		nce.creatures.resize(7);
 		for(int vv = 0; vv < 7; ++vv)

+ 33 - 19
lib/mapping/MapFormatH3M.h

@@ -25,6 +25,7 @@ class CGTownInstance;
 class CCreatureSet;
 class CInputStream;
 class TextIdentifier;
+class CGPandoraBox;
 
 class ObjectInstanceID;
 class BuildingID;
@@ -153,6 +154,35 @@ 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(const int3 & position);
+	CGObjectInstance * readScholar(const int3 & position);
+	CGObjectInstance * readGarrison(const int3 & position);
+	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(const int3 & position);
+	CGObjectInstance * readHeroPlaceholder(const int3 & position);
+	CGObjectInstance * readGrail(const int3 & position);
+	CGObjectInstance * readPyramid(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
+	CGObjectInstance * readBorderGuard(const int3 & position);
+	CGObjectInstance * readBorderGate(const int3 & position);
+	CGObjectInstance * readQuestGuard(const int3 & position);
+	CGObjectInstance * readShipyard(const int3 & position);
+	CGObjectInstance * readLighthouse(const int3 & position);
+	CGObjectInstance * readBlank(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);
+
 	/**
 	 * Reads a creature set.
 	 *
@@ -162,19 +192,11 @@ private:
 	void readCreatureSet(CCreatureSet * out, int number);
 
 	/**
-	 * Reads a hero.
-	 *
-	 * @param idToBeGiven the object id which should be set for the hero
-	 * @return a object instance
-	 */
-	CGObjectInstance * readHero(const ObjectInstanceID & idToBeGiven, const int3 & initialPos);
-
-	/**
-	 * Reads a seer hut.
+	 * Reads a quest for the given quest guard.
 	 *
-	 * @return the initialized seer hut object
+	 * @param guard the quest guard where that quest should be applied to
 	 */
-	CGSeerHut * readSeerHut(const int3 & position);
+	void readBoxContent(CGPandoraBox * object, const int3 & position);
 
 	/**
 	 * Reads a quest for the given quest guard.
@@ -183,14 +205,6 @@ 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);
-
 	/**
 	 * Converts buildings to the specified castle id.
 	 *