Преглед изворни кода

Implemented (basic) object construction and placement
* contain refactoring of RMG and class handling (will be more)

AlexVinS пре 10 година
родитељ
комит
2d5a366e6c

+ 47 - 22
lib/CArtHandler.cpp

@@ -207,6 +207,28 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
 
 	artifacts.push_back(object);
 
+	VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)
+	{
+		JsonNode conf;
+		conf.setMeta(scope);
+
+		VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);
+
+		if (!object->advMapDef.empty())
+		{
+			JsonNode templ;
+			templ.setMeta(scope);
+			templ["animation"].String() = object->advMapDef;
+
+			// add new template.
+			// Necessary for objects added via mods that don't have any templates in H3
+			VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);
+		}
+		// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
+		if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())
+			VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);
+	});
+
 	registerObject(scope, "artifact", object->identifier, object->id);
 }
 
@@ -219,6 +241,27 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
 	assert(artifacts[index] == nullptr); // ensure that this id was not loaded before
 	artifacts[index] = object;
 
+	VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)
+	{
+		JsonNode conf;
+		conf.setMeta(scope);
+
+		VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);
+
+		if (!object->advMapDef.empty())
+		{
+			JsonNode templ;
+			templ.setMeta(scope);
+			templ["animation"].String() = object->advMapDef;
+
+			// add new template.
+			// Necessary for objects added via mods that don't have any templates in H3
+			VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);
+		}
+		// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
+		if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())
+			VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);
+	});
 	registerObject(scope, "artifact", object->identifier, object->id);
 }
 
@@ -280,16 +323,16 @@ ArtifactPosition CArtHandler::stringToSlot(std::string slotName)
 
 void CArtHandler::addSlot(CArtifact * art, const std::string & slotID)
 {
-	static const std::vector<ArtifactPosition> miscSlots = 
+	static const std::vector<ArtifactPosition> miscSlots =
 	{
 		ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5
 	};
-	
+
 	static const std::vector<ArtifactPosition> ringSlots =
 	{
 		ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING
 	};
-	
+
 	if (slotID == "MISC")
 	{
 		vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots);
@@ -323,7 +366,7 @@ void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node)
 CArtifact::EartClass CArtHandler::stringToClass(std::string className)
 {
 	static const std::map<std::string, CArtifact::EartClass> artifactClassMap =
-	{	
+	{
 		{"TREASURE", CArtifact::ART_TREASURE},
 		{"MINOR", CArtifact::ART_MINOR},
 		{"MAJOR", CArtifact::ART_MAJOR},
@@ -694,24 +737,6 @@ void CArtHandler::afterLoadFinalization()
 			bonus->sid = art->id;
 		}
 	}
-
-	for (CArtifact * art : artifacts)
-	{
-		VLC->objtypeh->loadSubObject(art->Name(), JsonNode(), Obj::ARTIFACT, art->id.num);
-
-		if (!art->advMapDef.empty())
-		{
-			JsonNode templ;
-			templ["animation"].String() = art->advMapDef;
-
-			// add new template.
-			// Necessary for objects added via mods that don't have any templates in H3
-			VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->addTemplate(templ);
-		}
-		// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
-		if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->getTemplates().empty())
-			VLC->objtypeh->removeSubObject(Obj::ARTIFACT, art->id);
-	}
 }
 
 CArtifactInstance::CArtifactInstance()

+ 38 - 15
lib/CCreatureHandler.cpp

@@ -166,12 +166,12 @@ static void AddAbility(CCreature *cre, const JsonVector &ability_vec)
 	}
 
 	nsf->type = it->second;
-	
+
 	JsonUtils::parseTypedBonusShort(ability_vec,nsf);
 
 	nsf->source = Bonus::CREATURE_ABILITY;
 	nsf->sid = cre->idNumber;
-	
+
 	cre->addNewBonus(nsf);
 }
 
@@ -363,6 +363,24 @@ void CCreatureHandler::loadObject(std::string scope, std::string name, const Jso
 
 	creatures.push_back(object);
 
+	VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
+	{
+		JsonNode conf;
+		conf.setMeta(scope);
+
+		VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num);
+		if (!object->advMapDef.empty())
+		{
+			JsonNode templ;
+			templ["animation"].String() = object->advMapDef;
+			VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ);
+		}
+
+		// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
+		if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty())
+			VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num);
+	});
+
 	registerObject(scope, "creature", name, object->idNumber);
 
 	for(auto node : data["extraNames"].Vector())
@@ -385,6 +403,24 @@ void CCreatureHandler::loadObject(std::string scope, std::string name, const Jso
 	assert(creatures[index] == nullptr); // ensure that this id was not loaded before
 	creatures[index] = object;
 
+	VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
+	{
+		JsonNode conf;
+		conf.setMeta(scope);
+
+		VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num);
+		if (!object->advMapDef.empty())
+		{
+			JsonNode templ;
+			templ["animation"].String() = object->advMapDef;
+			VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ);
+		}
+
+		// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
+		if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty())
+			VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num);
+	});
+
 	registerObject(scope, "creature", name, object->idNumber);
 	for(auto & node : data["extraNames"].Vector())
 	{
@@ -1124,20 +1160,7 @@ void CCreatureHandler::buildBonusTreeForTiers()
 
 void CCreatureHandler::afterLoadFinalization()
 {
-	for (CCreature * crea : creatures)
-	{
-		VLC->objtypeh->loadSubObject(crea->nameSing, JsonNode(), Obj::MONSTER, crea->idNumber.num);
-		if (!crea->advMapDef.empty())
-		{
-			JsonNode templ;
-			templ["animation"].String() = crea->advMapDef;
-			VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber)->addTemplate(templ);
-		}
 
-		// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
-		if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber.num)->getTemplates().empty())
-			VLC->objtypeh->removeSubObject(Obj::MONSTER, crea->idNumber.num);
-	}
 }
 
 void CCreatureHandler::deserializationFix()

+ 7 - 0
lib/CHeroHandler.cpp

@@ -205,6 +205,13 @@ void CHeroClassHandler::loadObject(std::string scope, std::string name, const Js
 		VLC->objtypeh->loadSubObject(name, classConf, index, object->id);
 	});
 
+//	VLC->modh->identifiers.requestIdentifier(scope, "object", "prison", [=](si32 index)
+//	{
+//		JsonNode conf;
+//		conf.setMeta(scope);
+//		VLC->objtypeh->loadSubObject(name, conf, index, object->id);
+//	});
+
 	VLC->modh->identifiers.registerObject(scope, "heroClass", name, object->id);
 }
 

+ 7 - 6
lib/CModHandler.cpp

@@ -101,9 +101,9 @@ void CIdentifierStorage::requestIdentifier(std::string scope, std::string type,
 
 void CIdentifierStorage::requestIdentifier(std::string scope, std::string fullName, const std::function<void(si32)>& callback)
 {
-	auto scopeAndFullName = splitString(fullName, ':');	
-	auto typeAndName = splitString(scopeAndFullName.second, '.');	
-	
+	auto scopeAndFullName = splitString(fullName, ':');
+	auto typeAndName = splitString(scopeAndFullName.second, '.');
+
 	requestIdentifier(ObjectCallback(scope, scopeAndFullName.first, typeAndName.first, typeAndName.second, callback, false));
 }
 
@@ -186,6 +186,7 @@ void CIdentifierStorage::registerObject(std::string scope, std::string type, std
 	checkIdentifier(fullID);
 
 	registeredObjects.insert(std::make_pair(fullID, data));
+	logGlobal->traceStream() << scope << "::" << fullID;
 }
 
 std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdentifiers(const ObjectCallback & request)
@@ -331,11 +332,11 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
 {
 	ModInfo & modInfo = modData[modName];
 	bool result = true;
-	
+
 	auto performValidate = [&,this](JsonNode & data, const std::string & name){
 		handler->beforeValidate(data);
 		if (validate)
-			result &= JsonUtils::validate(data, "vcmi:" + objectName, name);	
+			result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
 	};
 
 	// apply patches
@@ -355,7 +356,7 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
 			if (originalData.size() > index)
 			{
 				JsonUtils::merge(originalData[index], data);
-				
+
 				performValidate(originalData[index],name);
 				handler->loadObject(modName, name, originalData[index], index);
 

+ 50 - 23
lib/mapObjects/CObjectClassesHandler.cpp

@@ -74,7 +74,6 @@ CObjectClassesHandler::CObjectClassesHandler()
 	SET_HANDLER("pandora", CGPandoraBox);
 	SET_HANDLER("pickable", CGPickable);
 	SET_HANDLER("prison", CGHeroInstance);
-	SET_HANDLER("prison", CGHeroInstance);
 	SET_HANDLER("questGuard", CGQuestGuard);
 	SET_HANDLER("resource", CGResource);
 	SET_HANDLER("scholar", CGScholar);
@@ -148,17 +147,18 @@ si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 defaultID)
 	return defaultID; // some H3M objects loaded, first modded found
 }
 
-void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContainter * obj)
+void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj)
 {
 	if (!handlerConstructors.count(obj->handlerName))
 	{
 		logGlobal->errorStream() << "Handler with name " << obj->handlerName << " was not found!";
 		return;
 	}
-	si32 id = selectNextID(entry["index"], obj->objects, 1000);
+	si32 id = selectNextID(entry["index"], obj->subObjects, 1000);
 
 	auto handler = handlerConstructors.at(obj->handlerName)();
 	handler->setType(obj->id, id);
+	handler->setTypeName(obj->identifier, identifier);
 
 	if (customNames.count(obj->id) && customNames.at(obj->id).size() > id)
 		handler->init(entry, customNames.at(obj->id).at(id));
@@ -175,50 +175,49 @@ void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContai
 		legacyTemplates.erase(range.first, range.second);
 	}
 
-	logGlobal->debugStream() << "Loaded object " << obj->id << ":" << id;
-	assert(!obj->objects.count(id)); // DO NOT override
-	obj->objects[id] = handler;
+	logGlobal->debugStream() << "Loaded object " << obj->identifier << "(" << obj->id << ")" << ":" << identifier << "(" << id << ")" ;
+	assert(!obj->subObjects.count(id)); // DO NOT override
+	obj->subObjects[id] = handler;
+	obj->subIds[identifier] = id;//todo: scope
 }
 
-CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json)
+CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json, const std::string & name)
 {
 	auto obj = new ObjectContainter();
+	obj->identifier = name;
 	obj->name = json["name"].String();
 	obj->handlerName = json["handler"].String();
 	obj->base = json["base"];
 	obj->id = selectNextID(json["index"], objects, 256);
 	for (auto entry : json["types"].Struct())
 	{
-		loadObjectEntry(entry.second, obj);
+		loadObjectEntry(entry.first, entry.second, obj);
 	}
 	return obj;
 }
 
 void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
 {
-	auto object = loadFromJson(data);
+	auto object = loadFromJson(data, name);
 	objects[object->id] = object;
-
 	VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
 }
 
 void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
 {
-	auto object = loadFromJson(data);
-
+	auto object = loadFromJson(data, name);
 	assert(objects[index] == nullptr); // ensure that this id was not loaded before
 	objects[index] = object;
-
 	VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
 }
 
-void CObjectClassesHandler::loadSubObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID)
+void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID)
 {
 	config.setType(JsonNode::DATA_STRUCT); // ensure that input is not NULL
 	assert(objects.count(ID));
 	if (subID)
 	{
-		assert(objects.at(ID)->objects.count(subID.get()) == 0);
+		assert(objects.at(ID)->subObjects.count(subID.get()) == 0);
 		assert(config["index"].isNull());
 		config["index"].Float() = subID.get();
 	}
@@ -227,14 +226,14 @@ void CObjectClassesHandler::loadSubObject(std::string name, JsonNode config, si3
 	JsonUtils::inherit(config, objects.at(ID)->base);
 	config.setMeta(oldMeta);
 
-	loadObjectEntry(config, objects[ID]);
+	loadObjectEntry(identifier, config, objects[ID]);
 }
 
 void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID)
 {
 	assert(objects.count(ID));
-	assert(objects.at(ID)->objects.count(subID));
-	objects.at(ID)->objects.erase(subID);
+	assert(objects.at(ID)->subObjects.count(subID));
+	objects.at(ID)->subObjects.erase(subID); //TODO: cleanup string id map
 }
 
 std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
@@ -246,14 +245,28 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype)
 {
 	if (objects.count(type))
 	{
-		if (objects.at(type)->objects.count(subtype))
-			return objects.at(type)->objects.at(subtype);
+		if (objects.at(type)->subObjects.count(subtype))
+			return objects.at(type)->subObjects.at(subtype);
+	}
+	logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype;
+	throw std::runtime_error("Object type handler not found");
+	return nullptr;
+}
+
+TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string type, std::string subtype) const
+{
+	boost::optional<si32> id = VLC->modh->identifiers.getIdentifier("core", "object", type, false);
+	if(id)
+	{
+		si32 subId = objects.at(id.get())->subIds.at(subtype);
+		return objects.at(id.get())->subObjects.at(subId);
 	}
 	logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype;
-	assert(0); // FIXME: throw error?
+	throw std::runtime_error("Object type handler not found");
 	return nullptr;
 }
 
+
 std::set<si32> CObjectClassesHandler::knownObjects() const
 {
 	std::set<si32> ret;
@@ -270,7 +283,7 @@ std::set<si32> CObjectClassesHandler::knownSubObjects(si32 primaryID) const
 
 	if (objects.count(primaryID))
 	{
-		for (auto entry : objects.at(primaryID)->objects)
+		for (auto entry : objects.at(primaryID)->subObjects)
 			ret.insert(entry.first);
 	}
 	return ret;
@@ -292,7 +305,7 @@ void CObjectClassesHandler::afterLoadFinalization()
 {
 	for (auto entry : objects)
 	{
-		for (auto obj : entry.second->objects)
+		for (auto obj : entry.second->subObjects)
 		{
 			obj.second->afterLoadFinalization();
 			if (obj.second->getTemplates().empty())
@@ -331,6 +344,12 @@ void AObjectTypeHandler::setType(si32 type, si32 subtype)
 	this->subtype = subtype;
 }
 
+void AObjectTypeHandler::setTypeName(std::string type, std::string subtype)
+{
+	this->typeName = type;
+	this->subTypeName = subtype;
+}
+
 static ui32 loadJsonOrMax(const JsonNode & input)
 {
 	if (input.isNull())
@@ -377,6 +396,14 @@ bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, const ObjectTemp
 	return false; // by default there are no overrides
 }
 
+void AObjectTypeHandler::preInitObject(CGObjectInstance * obj) const
+{
+	obj->ID = Obj(type);
+	obj->subID = subtype;
+	obj->typeName = typeName;
+	obj->subTypeName = subTypeName;
+}
+
 void AObjectTypeHandler::initTypeData(const JsonNode & input)
 {
 	// empty implementation for overrides

+ 24 - 10
lib/mapObjects/CObjectClassesHandler.h

@@ -104,11 +104,14 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
 	si32 type;
 	si32 subtype;
 
+	std::string typeName;
+	std::string subTypeName;
+
 	JsonNode base; /// describes base template
 
 	std::vector<ObjectTemplate> templates;
 protected:
-
+	void preInitObject(CGObjectInstance * obj) const;
 	virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
 
 	/// initialization for classes that inherit this one
@@ -117,6 +120,7 @@ public:
 	virtual ~AObjectTypeHandler(){}
 
 	void setType(si32 type, si32 subtype);
+	void setTypeName(std::string type, std::string subtype);
 
 	/// loads generic data from Json structure and passes it towards type-specific constructors
 	void init(const JsonNode & input, boost::optional<std::string> name = boost::optional<std::string>());
@@ -149,12 +153,16 @@ public:
 	/// This should set remaining properties, including randomized or depending on map
 	virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0;
 
-	/// Returns object configuration, if available. Othervice returns NULL
+	/// Returns object configuration, if available. Otherwise returns NULL
 	virtual std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const = 0;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & type & subtype & templates & rmgInfo & objectName;
+		if(version >= 755)
+		{
+			h & typeName & subTypeName;
+		}
 	}
 };
 
@@ -167,16 +175,21 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
 	struct ObjectContainter
 	{
 		si32 id;
-
+		std::string identifier;
 		std::string name; // human-readable name
-		std::string handlerName; // ID of handler that controls this object, shoul be determined using hadlerConstructor map
+		std::string handlerName; // ID of handler that controls this object, should be determined using handlerConstructor map
 
 		JsonNode base;
-		std::map<si32, TObjectTypeHandler> objects;
+		std::map<si32, TObjectTypeHandler> subObjects;
+		std::map<std::string, si32> subIds;//full id from core scope -> subtype
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
-			h & name & handlerName & base & objects;
+			h & name & handlerName & base & subObjects;
+			if(version >= 755)
+			{
+				h & identifier & subIds;
+			}
 		}
 	};
 
@@ -194,8 +207,8 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
 	/// format: customNames[primaryID][secondaryID] -> name
 	std::map<si32, std::vector<std::string>> customNames;
 
-	void loadObjectEntry(const JsonNode & entry, ObjectContainter * obj);
-	ObjectContainter * loadFromJson(const JsonNode & json);
+	void loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj);
+	ObjectContainter * loadFromJson(const JsonNode & json, const std::string & name);
 public:
 	CObjectClassesHandler();
 
@@ -204,7 +217,7 @@ public:
 	void loadObject(std::string scope, std::string name, const JsonNode & data) override;
 	void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
 
-	void loadSubObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>());
+	void loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>());
 	void removeSubObject(si32 ID, si32 subID);
 
 	void beforeValidate(JsonNode & object) override;
@@ -218,10 +231,11 @@ public:
 
 	/// returns handler for specified object (ID-based). ObjectHandler keeps ownership
 	TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const;
+	TObjectTypeHandler getHandlerFor(std::string type, std::string subtype) const;
 
 	std::string getObjectName(si32 type) const;
 	std::string getObjectName(si32 type, si32 subtype) const;
-	
+
 	/// Returns handler string describing the handler (for use in client)
 	std::string getObjectHandlerName(si32 type) const;
 

+ 13 - 0
lib/mapObjects/CObjectHandler.cpp

@@ -326,7 +326,14 @@ bool CGObjectInstance::passableFor(PlayerColor color) const
 
 void CGObjectInstance::writeJson(JsonNode & json, bool withState) const
 {
+	logGlobal->debugStream() <<"Save: [" << pos << "] " << id << " " << ID << " " << subID << " " << typeName << " " << subTypeName;
+
 	json.setType(JsonNode::DATA_STRUCT);
+	json["type"].String() = typeName;
+	json["subType"].String() = subTypeName;
+	json["x"].Float() = pos.x;
+	json["y"].Float() = pos.y;
+	json["l"].Float() = pos.z;
 
 	appearance.writeJson(json["template"]);
 	writeJsonOptions(json["options"]);
@@ -341,10 +348,16 @@ void CGObjectInstance::readJson(const JsonNode & json, bool withState)
 		logGlobal->error("Invalid object instance data");
 		return;
 	}
+	pos.x = json["x"].Float();
+	pos.y = json["y"].Float();
+	pos.z = json["l"].Float();
+
 	appearance.readJson(json["template"]);
 	readJsonOptions(json["options"]);
 	if(withState)
 		readJsonState(json["state"]);
+
+	logGlobal->debugStream() <<"Load: [" << pos << "] " << id << " " << ID << " " << subID << " " << typeName << " " << subTypeName;
 }
 
 void CGObjectInstance::writeJsonOptions(JsonNode & json) const

+ 8 - 0
lib/mapObjects/CObjectHandler.h

@@ -113,6 +113,9 @@ public:
 	/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
 	bool blockVisit;
 
+	std::string typeName;
+	std::string subTypeName;
+
 	CGObjectInstance();
 	~CGObjectInstance();
 
@@ -171,6 +174,11 @@ public:
 	{
 		h & pos & ID & subID & id & tempOwner & blockVisit & appearance;
 		//definfo is handled by map serializer
+
+		if(version >= 755)
+		{
+			h & typeName & subTypeName;
+		}
 	}
 
 	///Entry point of Json serialization

+ 1 - 0
lib/mapObjects/CRewardableConstructor.cpp

@@ -188,6 +188,7 @@ void CRewardableConstructor::initTypeData(const JsonNode & config)
 CGObjectInstance * CRewardableConstructor::create(ObjectTemplate tmpl) const
 {
 	auto ret = new CRewardableObject();
+	preInitObject(ret);
 	ret->appearance = tmpl;
 	return ret;
 }

+ 1 - 2
lib/mapObjects/CommonConstructors.h

@@ -31,8 +31,7 @@ protected:
 	ObjectType * createTyped(ObjectTemplate tmpl) const
 	{
 		auto obj = new ObjectType();
-		obj->ID = tmpl.id;
-		obj->subID = tmpl.subid;
+		preInitObject(obj);
 		obj->appearance = tmpl;
 		return obj;
 	}

+ 14 - 3
lib/mapping/MapFormatJson.cpp

@@ -284,6 +284,13 @@ void CMapLoaderJson::readMap()
 	map->initTerrain();
 	readTerrain();
 	readObjects();
+
+	// Calculate blocked / visitable positions
+	for(auto & elem : map->objects)
+	{
+		map->addBlockVisTiles(elem);
+	}
+	map->calculateGuardingGreaturePositions();
 }
 
 void CMapLoaderJson::readHeader()
@@ -569,6 +576,7 @@ CMapLoaderJson::MapObjectLoader::MapObjectLoader(CMapLoaderJson * _owner, const
 
 void CMapLoaderJson::MapObjectLoader::construct()
 {
+	//TODO:consider move to ObjectTypeHandler
 	//find type handler
 	std::string typeName = configuration["type"].String(), subTypeName = configuration["subType"].String();
 	if(typeName.empty())
@@ -584,13 +592,16 @@ void CMapLoaderJson::MapObjectLoader::construct()
 
 	si32 type = owner->getIdentifier("object", typeName);
 
-//	VLC->objtypeh->getHandlerFor()
-//TODO:MapObjectLoader::construct()
+	handler = VLC->objtypeh->getHandlerFor(typeName, subTypeName);
+
+	instance = handler->create(ObjectTemplate());
+	instance->id = ObjectInstanceID(owner->map->objects.size());
+	owner->map->objects.push_back(instance);
 }
 
 void CMapLoaderJson::MapObjectLoader::configure()
 {
-//TODO:MapObjectLoader::configure()
+	instance->readJson(configuration, false);
 }
 
 void CMapLoaderJson::readObjects()

+ 14 - 15
lib/rmg/CMapGenerator.cpp

@@ -32,7 +32,7 @@ void CMapGenerator::foreachDirectNeighbour(const int3& pos, std::function<void(i
 		int3 n = pos + dir;
 		if(map->isInTheMap(n))
 			foo(n);
-	}	
+	}
 }
 
 
@@ -189,7 +189,7 @@ void CMapGenerator::addPlayerInfo()
 			playerCount = mapGenOptions->getCompOnlyPlayerCount();
 			teamCount = mapGenOptions->getCompOnlyTeamCount();
 		}
-		
+
 		if(playerCount == 0)
 		{
 			continue;
@@ -259,7 +259,7 @@ void CMapGenerator::genZones()
 }
 
 void CMapGenerator::fillZones()
-{	
+{
 	//init native town count with 0
 	for (auto faction : VLC->townh->getAllowedFactions())
 		zonesPerFaction[faction] = 0;
@@ -495,8 +495,8 @@ void CMapGenerator::createConnections()
 					zoneA->addMonster (this, guardPos, connection.getGuardStrength(), false, true);
 					//zones can make paths only in their own area
 					zoneA->crunchPath(this, guardPos, posA, true, zoneA->getFreePaths()); //make connection towards our zone center
-					zoneB->crunchPath(this, guardPos, posB, true, zoneB->getFreePaths()); //make connection towards other zone center		
-					
+					zoneB->crunchPath(this, guardPos, posB, true, zoneB->getFreePaths()); //make connection towards other zone center
+
 					zoneA->addRoadNode(guardPos);
 					zoneB->addRoadNode(guardPos);
 					break; //we're done with this connection
@@ -504,7 +504,7 @@ void CMapGenerator::createConnections()
 			}
 		}
 		else //create subterranean gates between two zones
-		{	
+		{
 			//find point on the path between zones
 			float3 offset (posB.x - posA.x, posB.y - posA.y, 0);
 
@@ -563,15 +563,14 @@ void CMapGenerator::createConnections()
 		}
 		if (!guardPos.valid())
 		{
-			auto teleport1 = new CGMonolith;
-			teleport1->ID = Obj::MONOLITH_TWO_WAY;
-			teleport1->subID = getNextMonlithIndex();
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, getNextMonlithIndex());
+			auto teleport1 = factory->create(ObjectTemplate());
 
-			auto teleport2 = new CGMonolith(*teleport1);
+			auto teleport2 = factory->create(ObjectTemplate());
 
 			zoneA->addRequiredObject (teleport1, connection.getGuardStrength());
 			zoneB->addRequiredObject (teleport2, connection.getGuardStrength());
-		}		
+		}
 	}
 }
 
@@ -590,7 +589,7 @@ void CMapGenerator::addHeaderInfo()
 void CMapGenerator::checkIsOnMap(const int3& tile) const
 {
 	if (!map->isInTheMap(tile))
-		throw  rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile));	
+		throw  rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile));
 }
 
 
@@ -633,8 +632,8 @@ bool CMapGenerator::isUsed(const int3 &tile) const
 bool CMapGenerator::isRoad(const int3& tile) const
 {
 	checkIsOnMap(tile);
-	
-	return tiles[tile.x][tile.y][tile.z].isRoad();	
+
+	return tiles[tile.x][tile.y][tile.z].isRoad();
 }
 
 void CMapGenerator::setOccupied(const int3 &tile, ETileType::ETileType state)
@@ -648,7 +647,7 @@ void CMapGenerator::setRoad(const int3& tile, ERoadType::ERoadType roadType)
 {
 	checkIsOnMap(tile);
 
-	tiles[tile.x][tile.y][tile.z].setRoadType(roadType);	
+	tiles[tile.x][tile.y][tile.z].setRoadType(roadType);
 }
 
 

+ 104 - 113
lib/rmg/CRmgTemplateZone.cpp

@@ -86,7 +86,7 @@ void CRmgTemplateZone::CTownInfo::setCastleDensity(int value)
 	castleDensity = value;
 }
 
-CTileInfo::CTileInfo():nearestObjectDistance(INT_MAX), terrain(ETerrainType::WRONG),roadType(ERoadType::NO_ROAD) 
+CTileInfo::CTileInfo():nearestObjectDistance(INT_MAX), terrain(ETerrainType::WRONG),roadType(ERoadType::NO_ROAD)
 {
 	occupied = ETileType::POSSIBLE; //all tiles are initially possible to place objects or passages
 }
@@ -638,7 +638,7 @@ do not leave zone border
 		}
 
 		auto lastDistance = distance;
-			
+
 		auto processNeighbours = [this, gen, &currentPos, dst, &distance, &result, &end, clearedTiles](int3 &pos)
 		{
 			if (!result) //not sure if lambda is worth it...
@@ -672,19 +672,19 @@ do not leave zone border
 				}
 			}
 		};
-		
+
 		if (onlyStraight)
 			gen->foreachDirectNeighbour (currentPos, processNeighbours);
 		else
 			gen->foreach_neighbour (currentPos,processNeighbours);
-						
+
 		int3 anotherPos(-1, -1, -1);
 
 		if (!(result || distance < lastDistance)) //we do not advance, use more advanced pathfinding algorithm?
 		{
 			//try any nearby tiles, even if its not closer than current
 			float lastDistance = 2 * distance; //start with significantly larger value
-			
+
 			auto processNeighbours2 = [this, gen, &currentPos, dst, &lastDistance, &anotherPos, &end, clearedTiles](int3 &pos)
 			{
 				if (currentPos.dist2dSQ(dst) < lastDistance) //try closest tiles from all surrounding unused tiles
@@ -700,13 +700,13 @@ do not leave zone border
 						}
 					}
 				}
-			};			
+			};
 			if (onlyStraight)
 				gen->foreachDirectNeighbour(currentPos, processNeighbours2);
 			else
 				gen->foreach_neighbour(currentPos, processNeighbours2);
-						
-			
+
+
 			if (anotherPos.valid())
 			{
 				if (clearedTiles)
@@ -878,7 +878,7 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl
 
 			if (onlyStraight)
 				gen->foreachDirectNeighbour(currentNode, foo);
-			else 
+			else
 				gen->foreach_neighbour(currentNode, foo);
 		}
 
@@ -997,8 +997,8 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength,
 	static const float multiplier1[] = {0.5, 0.75, 1.0, 1.5, 1.5};
 	static const float multiplier2[] = {0.5, 0.75, 1.0, 1.0, 1.5};
 
-	int strength1 = std::max(0.f, (strength - value1[monsterStrength]) * multiplier1[monsterStrength]); 
-	int strength2 = std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength]); 
+	int strength1 = std::max(0.f, (strength - value1[monsterStrength]) * multiplier1[monsterStrength]);
+	int strength2 = std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength]);
 
 	strength = strength1 + strength2;
 	if (strength < 2000)
@@ -1031,10 +1031,9 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength,
 		amount = strength / VLC->creh->creatures[creId]->AIValue;
 	}
 
+	auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId);
 
-	auto guard = new CGCreature();
-	guard->ID = Obj::MONSTER;
-	guard->subID = creId;
+	auto guard = (CGCreature *) guardFactory->create(ObjectTemplate());
 	guard->character = CGCreature::HOSTILE;
 	auto  hlp = new CStackInstance(creId, amount);
 	//will be set during initialization
@@ -1132,7 +1131,7 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m
 			info.occupiedPositions.insert(visitablePos);
 
 			currentValue += oi.value;
-		
+
 			treasures[info.nextTreasurePos] = object;
 
 			//now find place for next object
@@ -1313,19 +1312,23 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
 	{
 		for (int i = 0; i < count; i++)
 		{
-			auto town = new CGTownInstance();
-			town->ID = Obj::TOWN;
+			si32 subType = townType;
 
-			if (this->townsAreSameType)
-				town->subID = townType;
-			else
+			if(totalTowns>0)
 			{
-				if (townTypes.size())
-					town->subID = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
-				else
-					town->subID = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
+				if(!this->townsAreSameType)
+				{
+					if (townTypes.size())
+						subType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
+					else
+						subType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
+				}
 			}
 
+			auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType);
+			auto town = (CGTownInstance *) townFactory->create(ObjectTemplate());
+			town->ID = Obj::TOWN;
+
 			town->tempOwner = player;
 			if (hasFort)
 				town->builtBuildings.insert(BuildingID::FORT);
@@ -1337,10 +1340,8 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
 					town->possibleSpells.push_back(spell->id);
 			}
 
-			if (!totalTowns) 
+			if (totalTowns <= 0)
 			{
-				//first town in zone sets the facton of entire zone
-				town->subID = townType;
 				//register MAIN town of zone
 				gen->registerZone(town->subID);
 				//first town in zone goes in the middle
@@ -1376,10 +1377,9 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
 			randomizeTownType(gen);
 		}
 
-		auto  town = new CGTownInstance();
-		town->ID = Obj::TOWN;
+		auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, townType);
 
-		town->subID = townType;
+		CGTownInstance * town = (CGTownInstance *) townFactory->create(ObjectTemplate());
 		town->tempOwner = player;
 		town->builtBuildings.insert(BuildingID::FORT);
 		town->builtBuildings.insert(BuildingID::DEFAULT);
@@ -1490,20 +1490,25 @@ void CRmgTemplateZone::paintZoneTerrain (CMapGenerator* gen, ETerrainType terrai
 
 bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
 {
-	std::vector<Res::ERes> required_mines;
-	required_mines.push_back(Res::ERes::WOOD);
-	required_mines.push_back(Res::ERes::ORE);
-
 	static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE};
 	static const Res::ERes preciousResources[] = {Res::ERes::GEMS, Res::ERes::CRYSTAL, Res::ERes::MERCURY, Res::ERes::SULFUR};
 
+	std::array<TObjectTypeHandler, 7> factory =
+	{
+		VLC->objtypeh->getHandlerFor(Obj::MINE, 0),
+		VLC->objtypeh->getHandlerFor(Obj::MINE, 1),
+		VLC->objtypeh->getHandlerFor(Obj::MINE, 2),
+		VLC->objtypeh->getHandlerFor(Obj::MINE, 3),
+		VLC->objtypeh->getHandlerFor(Obj::MINE, 4),
+		VLC->objtypeh->getHandlerFor(Obj::MINE, 5),
+		VLC->objtypeh->getHandlerFor(Obj::MINE, 6)
+	};
+
 	for (const auto & res : woodOre)
 	{
 		for (int i = 0; i < mines[res]; i++)
 		{
-			auto mine = new CGMine();
-			mine->ID = Obj::MINE;
-			mine->subID = static_cast<si32>(res);
+			auto mine = (CGMine *) factory.at(static_cast<si32>(res))->create(ObjectTemplate());
 			mine->producedResource = res;
 			mine->producedQuantity = mine->defaultResProduction();
 			if (!i)
@@ -1516,9 +1521,7 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
 	{
 		for (int i = 0; i < mines[res]; i++)
 		{
-			auto mine = new CGMine();
-			mine->ID = Obj::MINE;
-			mine->subID = static_cast<si32>(res);
+			auto mine = (CGMine *) factory.at(static_cast<si32>(res))->create(ObjectTemplate());
 			mine->producedResource = res;
 			mine->producedQuantity = mine->defaultResProduction();
 			addRequiredObject(mine, 3500);
@@ -1526,9 +1529,7 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
 	}
 	for (int i = 0; i < mines[Res::GOLD]; i++)
 	{
-		auto mine = new CGMine();
-		mine->ID = Obj::MINE;
-		mine->subID = static_cast<si32>(Res::GOLD);
+		auto mine = (CGMine *) factory.at(Res::GOLD)->create(ObjectTemplate());
 		mine->producedResource = Res::GOLD;
 		mine->producedQuantity = mine->defaultResProduction();
 		addRequiredObject(mine, 7000);
@@ -1540,7 +1541,7 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
 bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
 {
 	logGlobal->traceStream() << "Creating required objects";
-	
+
 	for(const auto &object : requiredObjects)
 	{
 		auto obj = object.first;
@@ -1577,16 +1578,16 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
 				break;
 		}
 
-	
+
 		placeObject(gen, obj, pos);
 		guardObject (gen, obj, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY), true);
-		//paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones	
+		//paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones
 	}
 
 	for (const auto &obj : closeObjects)
 	{
 		std::vector<int3> tiles(possibleTiles.begin(), possibleTiles.end()); //new tiles vector after each object has been placed
-		
+
 		// smallest distance to zone center, greatest distance to nearest object
 		auto isCloser = [this, gen](const int3 & lhs, const int3 & rhs) -> bool
 		{
@@ -1675,7 +1676,7 @@ void CRmgTemplateZone::createTreasures(CMapGenerator* gen)
 				return !gen->isPossible(tile);
 			});
 
-			
+
 			int3 treasureTilePos;
 			//If we are able to place at least one object with value lower than minGuardedValue, it's ok
 			do
@@ -1722,11 +1723,11 @@ void CRmgTemplateZone::createObstacles2(CMapGenerator* gen)
 	std::vector<obstaclePair> possibleObstacles;
 
 	//get all possible obstacles for this terrain
-	for (auto primaryID : VLC->objtypeh->knownObjects()) 
-	{ 
-		for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID)) 
-		{ 
-			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID); 
+	for (auto primaryID : VLC->objtypeh->knownObjects())
+	{
+		for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
+		{
+			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
 			if (handler->isStaticObject())
 			{
 				for (auto temp : handler->getTemplates())
@@ -1735,7 +1736,7 @@ void CRmgTemplateZone::createObstacles2(CMapGenerator* gen)
 						obstaclesBySize[temp.getBlockedOffsets().size()].push_back(temp);
 				}
 			}
-		} 
+		}
 	}
 	for (auto o : obstaclesBySize)
 	{
@@ -1789,13 +1790,13 @@ void CRmgTemplateZone::createObstacles2(CMapGenerator* gen)
 void CRmgTemplateZone::connectRoads(CMapGenerator* gen)
 {
 	logGlobal->debug("Started building roads");
-	
+
 	std::set<int3> roadNodesCopy(roadNodes);
 	std::set<int3> processed;
-	
+
 	while(!roadNodesCopy.empty())
 	{
-		int3 node = *roadNodesCopy.begin(); 
+		int3 node = *roadNodesCopy.begin();
 		roadNodesCopy.erase(node);
 		int3 cross(-1, -1, -1);
 
@@ -1818,13 +1819,13 @@ void CRmgTemplateZone::connectRoads(CMapGenerator* gen)
 			processed.insert(cross); //don't draw road starting at end point which is already connected
 			vstd::erase_if_present(roadNodesCopy, cross);
 		}
-		
-		processed.insert(node); 
+
+		processed.insert(node);
 	}
 
 	drawRoads(gen);
-	
-	logGlobal->debug("Finished building roads");	
+
+	logGlobal->debug("Finished building roads");
 }
 
 void CRmgTemplateZone::drawRoads(CMapGenerator* gen)
@@ -1832,7 +1833,7 @@ void CRmgTemplateZone::drawRoads(CMapGenerator* gen)
 	std::vector<int3> tiles;
 	for (auto tile : roads)
 	{
-		if(gen->map->isInTheMap(tile))	
+		if(gen->map->isInTheMap(tile))
 			tiles.push_back (tile);
 	}
 	for (auto tile : roadNodes)
@@ -1841,8 +1842,8 @@ void CRmgTemplateZone::drawRoads(CMapGenerator* gen)
 			tiles.push_back(tile);
 	}
 
-	gen->editManager->getTerrainSelection().setSelection(tiles);	
-	gen->editManager->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand);	
+	gen->editManager->getTerrainSelection().setSelection(tiles);
+	gen->editManager->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand);
 }
 
 
@@ -1852,7 +1853,7 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen)
 
 	//zone center should be always clear to allow other tiles to connect
 	gen->setOccupied(this->getPos(), ETileType::FREE);
-	freePaths.insert(pos); 
+	freePaths.insert(pos);
 
 	addAllPossibleObjects (gen);
 
@@ -1861,7 +1862,7 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen)
 	placeMines(gen);
 	createRequiredObjects(gen);
 	createTreasures(gen);
-	
+
 	logGlobal->infoStream() << boost::format ("Zone %d filled successfully") %id;
 	return true;
 }
@@ -2040,7 +2041,7 @@ void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance*
 		auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(terrainType);
 		if (templates.empty())
 			throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") %object->ID %object->subID %pos %terrainType));
-	
+
 		object->appearance = templates.front();
 	}
 
@@ -2059,7 +2060,7 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
 		points.insert(pos + object->getVisitableOffset());
 	points.insert(pos);
 	for(auto p : points)
-	{		
+	{
 		if (gen->map->isInTheMap(p))
 		{
 			gen->setOccupied(p, ETileType::USED);
@@ -2068,7 +2069,7 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
 	if (updateDistance)
 	{
 		for(auto tile : possibleTiles) //don't need to mark distance for not possible tiles
-		{		
+		{
 			si32 d = pos.dist2dSQ(tile); //optimization, only relative distance is interesting
 			gen->setNearestObjectDistance(tile, std::min<float>(d, gen->getNearestObjectDistance(tile)));
 		}
@@ -2080,7 +2081,7 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
 		logGlobal->warnStream() << boost::format("Placed Seer Hut at %s, quest artifact %d is %s") % object->pos % artid % VLC->arth->artifacts[artid]->Name();
 	}
 
-	
+
 	switch (object->ID)
 	{
 	case Obj::TOWN:
@@ -2093,10 +2094,10 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
 			addRoadNode(object->visitablePos());
 		}
 		break;
-	
+
 	default:
 		break;
-	}		
+	}
 }
 
 void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard)
@@ -2107,9 +2108,8 @@ void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance*
 
 void CRmgTemplateZone::placeSubterraneanGate(CMapGenerator* gen, int3 pos, si32 guardStrength)
 {
-	auto gate = new CGSubterraneanGate;
-	gate->ID = Obj::SUBTERRANEAN_GATE;
-	gate->subID = 0;
+	auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
+	auto gate = factory->create(ObjectTemplate());
 	placeObject (gen, gate, pos, true);
 	addToConnectLater (getAccessibleOffset (gen, gate->appearance, pos)); //guard will be placed on accessibleOffset
 	guardObject (gen, gate, guardStrength, true);
@@ -2123,7 +2123,7 @@ std::vector<int3> CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CG
 
 	auto tilesBlockedByObject = object->getBlockedPos(); //absolue value, as object is already placed
 
-	gen->foreach_neighbour(visitable, [&](int3& pos) 
+	gen->foreach_neighbour(visitable, [&](int3& pos)
 	{
 		if (gen->isPossible(pos) || gen->isFree(pos))
 		{
@@ -2166,7 +2166,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
 			if (!gen->isFree(pos))
 				gen->setOccupied(pos, ETileType::BLOCKED);
 		}
-		gen->foreach_neighbour (guardTile, [&](int3& pos) 
+		gen->foreach_neighbour (guardTile, [&](int3& pos)
 		{
 			if (gen->isPossible(pos))
 				gen->setOccupied (pos, ETileType::FREE);
@@ -2303,9 +2303,8 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CMapGenerator* gen, CTreasurePileIn
 		{
 			oi.generateObject = [minValue]() -> CGObjectInstance *
 			{
-				auto obj = new CGPandoraBox();
-				obj->ID = Obj::PANDORAS_BOX;
-				obj->subID = 0;
+				auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
+				auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
 				obj->resources[Res::GOLD] = minValue;
 				return obj;
 			};
@@ -2393,9 +2392,6 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 	{
 		oi.generateObject = [i, gen, this]() -> CGObjectInstance *
 		{
-			auto obj = new CGHeroInstance;
-			obj->ID = Obj::PRISON;
-
 			std::vector<ui32> possibleHeroes;
 			for (int j = 0; j < gen->map->allowedHeroes.size(); j++)
 			{
@@ -2404,6 +2400,10 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 			}
 
 			auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, gen->rand);
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0);
+			auto obj = (CGHeroInstance *) factory->create(ObjectTemplate());
+
+
 			obj->subID = hid; //will be initialized later
 			obj->exp = prisonExp[i];
 			obj->setOwner(PlayerColor::NEUTRAL);
@@ -2471,9 +2471,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 	{
 		oi.generateObject = [i, gen]() -> CGObjectInstance *
 		{
-			auto obj = new CGArtifact();
-			obj->ID = Obj::SPELL_SCROLL;
-			obj->subID = 0;
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::SPELL_SCROLL, 0);
+			auto obj = (CGArtifact *) factory->create(ObjectTemplate());
 			std::vector<SpellID> out;
 
 			for (auto spell : VLC->spellh->objects) //spellh size appears to be greater (?)
@@ -2499,9 +2498,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 	{
 		oi.generateObject = [i]() -> CGObjectInstance *
 		{
-			auto obj = new CGPandoraBox();
-			obj->ID = Obj::PANDORAS_BOX;
-			obj->subID = 0;
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
+			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
 			obj->resources[Res::GOLD] = i * 5000;
 			return obj;
 		};
@@ -2516,9 +2514,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 	{
 		oi.generateObject = [i]() -> CGObjectInstance *
 		{
-			auto obj = new CGPandoraBox();
-			obj->ID = Obj::PANDORAS_BOX;
-			obj->subID = 0;
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
+			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
 			obj->gainedExp = i * 5000;
 			return obj;
 		};
@@ -2564,9 +2561,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 
 		oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance *
 		{
-			auto obj = new CGPandoraBox();
-			obj->ID = Obj::PANDORAS_BOX;
-			obj->subID = 0;
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
+			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
 			auto stack = new CStackInstance(creature, creaturesAmount);
 			obj->creatures.putStack(SlotID(0), stack);
 			return obj;
@@ -2582,9 +2578,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 	{
 		oi.generateObject = [i, gen]() -> CGObjectInstance *
 		{
-			auto obj = new CGPandoraBox();
-			obj->ID = Obj::PANDORAS_BOX;
-			obj->subID = 0;
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
+			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
 
 			std::vector <CSpell *> spells;
 			for (auto spell : VLC->spellh->objects)
@@ -2612,9 +2607,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 	{
 		oi.generateObject = [i,gen]() -> CGObjectInstance *
 		{
-			auto obj = new CGPandoraBox();
-			obj->ID = Obj::PANDORAS_BOX;
-			obj->subID = 0;
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
+			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
 
 			std::vector <CSpell *> spells;
 			for (auto spell : VLC->spellh->objects)
@@ -2642,9 +2636,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 
 	oi.generateObject = [gen]() -> CGObjectInstance *
 	{
-		auto obj = new CGPandoraBox();
-		obj->ID = Obj::PANDORAS_BOX;
-		obj->subID = 0;
+		auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
+		auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
 
 		std::vector <CSpell *> spells;
 		for (auto spell : VLC->spellh->objects)
@@ -2716,9 +2709,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 
 			oi.generateObject = [creature, creaturesAmount, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
 			{
-				auto obj = new CGSeerHut();
-				obj->ID = Obj::SEER_HUT;
-				obj->subID = randomAppearance;
+				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
+				auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
 				obj->rewardType = CGSeerHut::CREATURE;
 				obj->rID = creature->idNumber;
 				obj->rVal = creaturesAmount;
@@ -2752,9 +2744,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 
 			oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
 			{
-				auto obj = new CGSeerHut();
-				obj->ID = Obj::SEER_HUT;
-				obj->subID = randomAppearance;
+				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
+				auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
+
 				obj->rewardType = CGSeerHut::EXPERIENCE;
 				obj->rID = 0; //unitialized?
 				obj->rVal = seerExpGold[i];
@@ -2774,9 +2766,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
 
 			oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
 			{
-				auto obj = new CGSeerHut();
-				obj->ID = Obj::SEER_HUT;
-				obj->subID = randomAppearance;
+				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
+				auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
 				obj->rewardType = CGSeerHut::RESOURCES;
 				obj->rID = Res::GOLD;
 				obj->rVal = seerExpGold[i];

+ 6 - 0
test/MapComparer.cpp

@@ -152,6 +152,12 @@ void MapComparer::compareObject(const CGObjectInstance * actual, const CGObjectI
 {
 	BOOST_CHECK_EQUAL(actual->getStringId(), expected->getStringId());
 	BOOST_CHECK_EQUAL(typeid(actual).name(), typeid(expected).name());//todo: remove and use just comparison
+
+	std::string actualFullID = boost::to_string(boost::format("%s(%d)|%s(%d)") % actual->typeName % actual->id % actual->subTypeName % actual->subID);
+	std::string expectedFullID = boost::to_string(boost::format("%s(%d)|%s(%d)") % expected->typeName % expected->id % expected->subTypeName % expected->subID);
+
+	BOOST_CHECK_EQUAL(actualFullID, expectedFullID);
+	BOOST_CHECK_EQUAL(actual->pos, expected->pos);
 }
 
 void MapComparer::compareObjects()