浏览代码

Switch ObjectTemplate to shared_ptr<const> (#870)

DjWarmonger 3 年之前
父节点
当前提交
3d2dc2335b

+ 9 - 9
client/Graphics.cpp

@@ -173,8 +173,8 @@ void Graphics::loadHeroAnimations()
 	{
 		for (auto & templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->getIndex())->getTemplates())
 		{
-			if (!heroAnimations.count(templ.animationFile))
-				heroAnimations[templ.animationFile] = loadHeroAnimation(templ.animationFile);
+			if (!heroAnimations.count(templ->animationFile))
+				heroAnimations[templ->animationFile] = loadHeroAnimation(templ->animationFile);
 		}
 	}
 
@@ -381,21 +381,21 @@ std::shared_ptr<CAnimation> Graphics::getAnimation(const CGObjectInstance* obj)
 	return getAnimation(obj->appearance);
 }
 
-std::shared_ptr<CAnimation> Graphics::getAnimation(const ObjectTemplate & info)
+std::shared_ptr<CAnimation> Graphics::getAnimation(std::shared_ptr<const ObjectTemplate> info)
 {
 	//the only(?) invisible object
-	if(info.id == Obj::EVENT)
+	if(info->id == Obj::EVENT)
 	{
 		return std::shared_ptr<CAnimation>();
 	}
 
-	if(info.animationFile.empty())
+	if(info->animationFile.empty())
 	{
-		logGlobal->warn("Def name for obj (%d,%d) is empty!", info.id, info.subid);
+		logGlobal->warn("Def name for obj (%d,%d) is empty!", info->id, info->subid);
 		return std::shared_ptr<CAnimation>();
 	}
 
-	std::shared_ptr<CAnimation> ret = mapObjectAnimations[info.animationFile];
+	std::shared_ptr<CAnimation> ret = mapObjectAnimations[info->animationFile];
 
 	//already loaded
 	if(ret)
@@ -404,8 +404,8 @@ std::shared_ptr<CAnimation> Graphics::getAnimation(const ObjectTemplate & info)
 		return ret;
 	}
 
-	ret = std::make_shared<CAnimation>(info.animationFile);
-	mapObjectAnimations[info.animationFile] = ret;
+	ret = std::make_shared<CAnimation>(info->animationFile);
+	mapObjectAnimations[info->animationFile] = ret;
 
 	ret->preload();
 	return ret;

+ 1 - 1
client/Graphics.h

@@ -98,7 +98,7 @@ public:
 	void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player
 
 	std::shared_ptr<CAnimation> getAnimation(const CGObjectInstance * obj);
-	std::shared_ptr<CAnimation> getAnimation(const ObjectTemplate & info);
+	std::shared_ptr<CAnimation> getAnimation(std::shared_ptr<const ObjectTemplate> info);
 };
 
 extern Graphics * graphics;

+ 3 - 3
client/mapHandler.cpp

@@ -979,7 +979,7 @@ CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findHeroBitmap(const CGH
 		if (hero->boat)
 			animation = graphics->boatAnimations[hero->boat->subID];
 		else
-			animation = graphics->heroAnimations[hero->appearance.animationFile];
+			animation = graphics->heroAnimations[hero->appearance->animationFile];
 
 		bool moving = !hero->isStanding;
 		int group = getHeroFrameGroup(hero->moveDir, moving);
@@ -1486,8 +1486,8 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj
 		return true;
 	if (!b)
 		return false;
-	if (a->appearance.printPriority != b->appearance.printPriority)
-		return a->appearance.printPriority > b->appearance.printPriority;
+	if (a->appearance->printPriority != b->appearance->printPriority)
+		return a->appearance->printPriority > b->appearance->printPriority;
 
 	if(a->pos.y != b->pos.y)
 		return a->pos.y < b->pos.y;

+ 1 - 1
lib/mapObjects/CGHeroInstance.cpp

@@ -533,7 +533,7 @@ void CGHeroInstance::initObj(CRandomGenerator & rand)
 	{
 		auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(cb->gameState()->getTile(visitablePos())->terType, this);
 		if (customApp)
-			appearance = customApp.get();
+			appearance = customApp;
 	}
 
 	//copy active (probably growing) bonuses from hero prototype to hero object

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -1133,7 +1133,7 @@ void CGTownInstance::updateAppearance()
 	//FIXME: not the best way to do this
 	auto app = VLC->objtypeh->getHandlerFor(ID, subID)->getOverride(cb->gameState()->getTile(visitablePos())->terType, this);
 	if (app)
-		appearance = app.get();
+		appearance = app;
 }
 
 std::string CGTownInstance::nodeName() const

+ 32 - 31
lib/mapObjects/CObjectClassesHandler.cpp

@@ -117,11 +117,11 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
 
 	for (size_t i=0; i<totalNumber; i++)
 	{
-		ObjectTemplate templ;
-		templ.readTxt(parser);
+		auto templ = new ObjectTemplate;
+		templ->readTxt(parser);
 		parser.endLine();
-		std::pair<si32, si32> key(templ.id.num, templ.subid);
-		legacyTemplates.insert(std::make_pair(key, templ));
+		std::pair<si32, si32> key(templ->id.num, templ->subid);
+		legacyTemplates.insert(std::make_pair(key, std::shared_ptr<const ObjectTemplate>(templ)));
 	}
 
 	std::vector<JsonNode> ret(dataSize);// create storage for 256 objects
@@ -448,7 +448,6 @@ AObjectTypeHandler::AObjectTypeHandler():
 
 AObjectTypeHandler::~AObjectTypeHandler()
 {
-
 }
 
 void AObjectTypeHandler::setType(si32 type, si32 subtype)
@@ -488,12 +487,12 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
 		entry.second.setType(JsonNode::JsonType::DATA_STRUCT);
 		JsonUtils::inherit(entry.second, base);
 
-		ObjectTemplate tmpl;
-		tmpl.id = Obj(type);
-		tmpl.subid = subtype;
-		tmpl.stringID = entry.first; // FIXME: create "fullID" - type.object.template?
-		tmpl.readJson(entry.second);
-		templates.push_back(tmpl);
+		auto tmpl = new ObjectTemplate;
+		tmpl->id = Obj(type);
+		tmpl->subid = subtype;
+		tmpl->stringID = entry.first; // FIXME: create "fullID" - type.object.template?
+		tmpl->readJson(entry.second);
+		templates.push_back(std::shared_ptr<const ObjectTemplate>(tmpl));
 	}
 
 	if (input["name"].isNull())
@@ -523,7 +522,7 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
 	initTypeData(input);
 }
 
-bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, const ObjectTemplate &) const
+bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const
 {
 	return false; // by default there are no overrides
 }
@@ -551,26 +550,28 @@ SObjectSounds AObjectTypeHandler::getSounds() const
 	return sounds;
 }
 
-void AObjectTypeHandler::addTemplate(const ObjectTemplate & templ)
+void AObjectTypeHandler::addTemplate(std::shared_ptr<const ObjectTemplate> templ)
 {
+	//Otherwise the template remains constant
+	auto ptr = const_cast<ObjectTemplate*>(templ.get());
+	ptr->id = Obj(type);
+	ptr->subid = subtype;
 	templates.push_back(templ);
-	templates.back().id = Obj(type);
-	templates.back().subid = subtype;
 }
 
 void AObjectTypeHandler::addTemplate(JsonNode config)
 {
 	config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not null
 	JsonUtils::inherit(config, base);
-	ObjectTemplate tmpl;
-	tmpl.id = Obj(type);
-	tmpl.subid = subtype;
-	tmpl.stringID = ""; // TODO?
-	tmpl.readJson(config);
-	templates.push_back(tmpl);
+	auto tmpl = new ObjectTemplate;
+	tmpl->id = Obj(type);
+	tmpl->subid = subtype;
+	tmpl->stringID = ""; // TODO?
+	tmpl->readJson(config);
+	templates.emplace_back(tmpl);
 }
 
-std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates() const
+std::vector<std::shared_ptr<const ObjectTemplate>> AObjectTypeHandler::getTemplates() const
 {
 	return templates;
 }
@@ -580,14 +581,14 @@ BattleField AObjectTypeHandler::getBattlefield() const
 	return battlefield ? BattleField::fromString(battlefield.get()) : BattleField::NONE;
 }
 
-std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & terrainType) const
+std::vector<std::shared_ptr<const ObjectTemplate>>AObjectTypeHandler::getTemplates(const Terrain & terrainType) const
 {
-	std::vector<ObjectTemplate> templates = getTemplates();
-	std::vector<ObjectTemplate> filtered;
+	std::vector<std::shared_ptr<const ObjectTemplate>> templates = getTemplates();
+	std::vector<std::shared_ptr<const ObjectTemplate>> filtered;
 
-	std::copy_if(templates.begin(), templates.end(), std::back_inserter(filtered), [&](const ObjectTemplate & obj)
+	std::copy_if(templates.begin(), templates.end(), std::back_inserter(filtered), [&](std::shared_ptr<const ObjectTemplate> obj)
 	{
-		return obj.canBePlacedAt(terrainType);
+		return obj->canBePlacedAt(terrainType);
 	});
 	// H3 defines allowed terrains in a weird way - artifacts, monsters and resources have faulty masks here
 	// Perhaps we should re-define faulty templates and remove this workaround (already done for resources)
@@ -597,15 +598,15 @@ std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & ter
 		return filtered;
 }
 
-boost::optional<ObjectTemplate> AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const
+std::shared_ptr<const ObjectTemplate> AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const
 {
-	std::vector<ObjectTemplate> ret = getTemplates(terrainType);
-	for (auto & tmpl : ret)
+	std::vector<std::shared_ptr<const ObjectTemplate>> ret = getTemplates(terrainType);
+	for (const auto & tmpl: ret)
 	{
 		if (objectFilter(object, tmpl))
 			return tmpl;
 	}
-	return boost::optional<ObjectTemplate>();
+	return std::shared_ptr<const ObjectTemplate>(); //empty
 }
 
 const RandomMapInfo & AObjectTypeHandler::getRMGInfo()

+ 13 - 9
lib/mapObjects/CObjectClassesHandler.h

@@ -144,7 +144,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
 
 	JsonNode base; /// describes base template
 
-	std::vector<ObjectTemplate> templates;
+	std::vector<std::shared_ptr<const ObjectTemplate>> templates;
 
 	SObjectSounds sounds;
 
@@ -154,7 +154,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
 
 protected:
 	void preInitObject(CGObjectInstance * obj) const;
-	virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
+	virtual bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const;
 
 	/// initialization for classes that inherit this one
 	virtual void initTypeData(const JsonNode & input);
@@ -177,17 +177,21 @@ public:
 	boost::optional<std::string> getCustomName() const;
 	SObjectSounds getSounds() const;
 
-	void addTemplate(const ObjectTemplate & templ);
+	void addTemplate(std::shared_ptr<const ObjectTemplate> templ);
 	void addTemplate(JsonNode config);
 
 	/// returns all templates matching parameters
-	std::vector<ObjectTemplate> getTemplates() const;
-	std::vector<ObjectTemplate> getTemplates(const Terrain & terrainType) const;
+	std::vector<std::shared_ptr<const ObjectTemplate>> getTemplates() const;
+	std::vector<std::shared_ptr<const ObjectTemplate>> getTemplates(const Terrain & terrainType) const;
+
+	/// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle)
+	/// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server)
+	std::shared_ptr<const ObjectTemplate> getOverride(const Terrain & terrainType, const CGObjectInstance * object) const;
+
 	BattleField getBattlefield() const;
 
 	/// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle)
 	/// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server)
-	boost::optional<ObjectTemplate> getOverride(const Terrain & terrainType, const CGObjectInstance * object) const;
 
 	const RandomMapInfo & getRMGInfo();
 
@@ -199,14 +203,14 @@ public:
 
 	/// Creates object and set up core properties (like ID/subID). Object is NOT initialized
 	/// to allow creating objects before game start (e.g. map loading)
-	virtual CGObjectInstance * create(const ObjectTemplate & tmpl) const = 0;
+	virtual CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const = 0;
 
 	/// Configures object properties. Should be re-entrable, resetting state of the object if necessarily
 	/// 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. Otherwise returns NULL
-	virtual std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const = 0;
+	virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const = 0;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -264,7 +268,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
 	std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
 
 	/// container with H3 templates, used only during loading, no need to serialize it
-	typedef std::multimap<std::pair<si32, si32>, ObjectTemplate> TTemplatesContainer;
+	typedef std::multimap<std::pair<si32, si32>, std::shared_ptr<const ObjectTemplate>> TTemplatesContainer;
 	TTemplatesContainer legacyTemplates;
 
 	/// contains list of custom names for H3 objects (e.g. Dwellings), used to load H3 data

+ 10 - 10
lib/mapObjects/CObjectHandler.cpp

@@ -152,24 +152,24 @@ void CGObjectInstance::setOwner(PlayerColor ow)
 }
 int CGObjectInstance::getWidth() const//returns width of object graphic in tiles
 {
-	return appearance.getWidth();
+	return appearance->getWidth();
 }
 int CGObjectInstance::getHeight() const //returns height of object graphic in tiles
 {
-	return appearance.getHeight();
+	return appearance->getHeight();
 }
 bool CGObjectInstance::visitableAt(int x, int y) const //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles)
 {
-	return appearance.isVisitableAt(pos.x - x, pos.y - y);
+	return appearance->isVisitableAt(pos.x - x, pos.y - y);
 }
 bool CGObjectInstance::blockingAt(int x, int y) const
 {
-	return appearance.isBlockedAt(pos.x - x, pos.y - y);
+	return appearance->isBlockedAt(pos.x - x, pos.y - y);
 }
 
 bool CGObjectInstance::coveringAt(int x, int y) const
 {
-	return appearance.isVisibleAt(pos.x - x, pos.y - y);
+	return appearance->isVisibleAt(pos.x - x, pos.y - y);
 }
 
 std::set<int3> CGObjectInstance::getBlockedPos() const
@@ -179,7 +179,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
 	{
 		for(int h=0; h<getHeight(); ++h)
 		{
-			if(appearance.isBlockedAt(w, h))
+			if(appearance->isBlockedAt(w, h))
 				ret.insert(int3(pos.x - w, pos.y - h, pos.z));
 		}
 	}
@@ -188,7 +188,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
 
 std::set<int3> CGObjectInstance::getBlockedOffsets() const
 {
-	return appearance.getBlockedOffsets();
+	return appearance->getBlockedOffsets();
 }
 
 void CGObjectInstance::setType(si32 ID, si32 subID)
@@ -264,7 +264,7 @@ int CGObjectInstance::getSightRadius() const
 
 int3 CGObjectInstance::getVisitableOffset() const
 {
-	return appearance.getVisitableOffset();
+	return appearance->getVisitableOffset();
 }
 
 void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const
@@ -350,7 +350,7 @@ int3 CGObjectInstance::visitablePos() const
 
 bool CGObjectInstance::isVisitable() const
 {
-	return appearance.isVisitable();
+	return appearance->isVisitable();
 }
 
 bool CGObjectInstance::passableFor(PlayerColor color) const
@@ -375,7 +375,7 @@ void CGObjectInstance::serializeJson(JsonSerializeFormat & handler)
 		handler.serializeInt("y", pos.y);
 		handler.serializeInt("l", pos.z);
 		JsonNode app;
-		appearance.writeJson(app, false);
+		appearance->writeJson(app, false);
 		handler.serializeRaw("template",app, boost::none);
 	}
 

+ 1 - 1
lib/mapObjects/CObjectHandler.h

@@ -123,7 +123,7 @@ public:
 	/// Index of object in map's list of objects
 	ObjectInstanceID id;
 	/// Defines appearance of object on map (animation, blocked tiles, blit order, etc)
-	ObjectTemplate appearance;
+	std::shared_ptr<const ObjectTemplate> appearance;
 	/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
 	bool blockVisit;
 

+ 2 - 2
lib/mapObjects/CRewardableConstructor.cpp

@@ -183,7 +183,7 @@ void CRewardableConstructor::initTypeData(const JsonNode & config)
 	objectInfo.init(config);
 }
 
-CGObjectInstance * CRewardableConstructor::create(const ObjectTemplate & tmpl) const
+CGObjectInstance * CRewardableConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
 {
 	auto ret = new CRewardableObject();
 	preInitObject(ret);
@@ -196,7 +196,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG
 	objectInfo.configureObject(dynamic_cast<CRewardableObject*>(object), rng);
 }
 
-std::unique_ptr<IObjectInfo> CRewardableConstructor::getObjectInfo(const ObjectTemplate & tmpl) const
+std::unique_ptr<IObjectInfo> CRewardableConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
 {
 	return std::unique_ptr<IObjectInfo>(new CRandomRewardObjectInfo(objectInfo));
 }

+ 2 - 2
lib/mapObjects/CRewardableConstructor.h

@@ -49,9 +49,9 @@ class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler
 public:
 	CRewardableConstructor();
 
-	CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
+	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
 
 	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 
-	std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override;
+	std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
 };

+ 1 - 1
lib/mapObjects/CRewardableObject.cpp

@@ -1122,7 +1122,7 @@ std::vector<int3> CGMagicSpring::getVisitableOffsets() const
 
 	for(int y = 0; y < 6; y++)
 		for (int x = 0; x < 8; x++) //starting from left
-			if (appearance.isVisitableAt(x, y))
+			if (appearance->isVisitableAt(x, y))
 				visitableTiles.push_back (int3(x, y , 0));
 
 	return visitableTiles;

+ 12 - 12
lib/mapObjects/CommonConstructors.cpp

@@ -56,7 +56,7 @@ void CTownInstanceConstructor::afterLoadFinalization()
 	}
 }
 
-bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, const ObjectTemplate & templ) const
+bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, std::shared_ptr<const ObjectTemplate> templ) const
 {
 	auto town = dynamic_cast<const CGTownInstance *>(object);
 
@@ -65,10 +65,10 @@ bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, con
 		return town->hasBuilt(id);
 	};
 
-	return filters.count(templ.stringID) != 0 && filters.at(templ.stringID).test(buildTest);
+	return filters.count(templ->stringID) != 0 && filters.at(templ->stringID).test(buildTest);
 }
 
-CGObjectInstance * CTownInstanceConstructor::create(const ObjectTemplate & tmpl) const
+CGObjectInstance * CTownInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
 {
 	CGTownInstance * obj = createTyped(tmpl);
 	obj->town = faction->town;
@@ -80,7 +80,7 @@ void CTownInstanceConstructor::configureObject(CGObjectInstance * object, CRando
 {
 	auto templ = getOverride(object->cb->getTile(object->pos)->terType, object);
 	if(templ)
-		object->appearance = templ.get();
+		object->appearance = templ;
 }
 
 CHeroInstanceConstructor::CHeroInstanceConstructor()
@@ -110,7 +110,7 @@ void CHeroInstanceConstructor::afterLoadFinalization()
 	}
 }
 
-bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, const ObjectTemplate & templ) const
+bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, std::shared_ptr<const ObjectTemplate> templ) const
 {
 	auto hero = dynamic_cast<const CGHeroInstance *>(object);
 
@@ -119,14 +119,14 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, con
 		return hero->type->ID == id;
 	};
 
-	if(filters.count(templ.stringID))
+	if(filters.count(templ->stringID))
 	{
-		return filters.at(templ.stringID).test(heroTest);
+		return filters.at(templ->stringID).test(heroTest);
 	}
 	return false;
 }
 
-CGObjectInstance * CHeroInstanceConstructor::create(const ObjectTemplate & tmpl) const
+CGObjectInstance * CHeroInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
 {
 	CGHeroInstance * obj = createTyped(tmpl);
 	obj->type = nullptr; //FIXME: set to valid value. somehow.
@@ -167,12 +167,12 @@ void CDwellingInstanceConstructor::initTypeData(const JsonNode & input)
 	guards = input["guards"];
 }
 
-bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance *, const ObjectTemplate &) const
+bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const
 {
 	return false;
 }
 
-CGObjectInstance * CDwellingInstanceConstructor::create(const ObjectTemplate & tmpl) const
+CGObjectInstance * CDwellingInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
 {
 	CGDwelling * obj = createTyped(tmpl);
 
@@ -272,7 +272,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
 	bankResetDuration = static_cast<si32>(input["resetDuration"].Float());
 }
 
-CGObjectInstance *CBankInstanceConstructor::create(const ObjectTemplate & tmpl) const
+CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
 {
 	return createTyped(tmpl);
 }
@@ -494,7 +494,7 @@ bool CBankInfo::givesSpells() const
 }
 
 
-std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(const ObjectTemplate & tmpl) const
+std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
 {
 	return std::unique_ptr<IObjectInfo>(new CBankInfo(levels));
 }

+ 26 - 12
lib/mapObjects/CommonConstructors.h

@@ -26,17 +26,31 @@ template<class ObjectType>
 class CDefaultObjectTypeHandler : public AObjectTypeHandler
 {
 protected:
-	ObjectType * createTyped(const ObjectTemplate & tmpl) const
+	ObjectType * createTyped(std::shared_ptr<const ObjectTemplate> tmpl /* = nullptr */) const
 	{
 		auto obj = new ObjectType();
 		preInitObject(obj);
-		obj->appearance = tmpl;
+
+		if (tmpl)
+		{
+			obj->appearance = tmpl;
+		}
+		else
+		{
+			auto templates = getTemplates();
+			if (templates.empty())
+			{
+				throw std::runtime_error("No handler for created object");
+			}
+			obj->appearance = templates.front(); //just any template for now, will be initialized later
+		}
+
 		return obj;
 	}
 public:
 	CDefaultObjectTypeHandler() {}
 
-	CGObjectInstance * create(const ObjectTemplate & tmpl) const override
+	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override
 	{
 		return createTyped(tmpl);
 	}
@@ -45,7 +59,7 @@ public:
 	{
 	}
 
-	virtual std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override
+	virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override
 	{
 		return nullptr;
 	}
@@ -62,7 +76,7 @@ class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance
 {
 	JsonNode filtersJson;
 protected:
-	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
+	bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const override;
 	void initTypeData(const JsonNode & input) override;
 
 public:
@@ -70,7 +84,7 @@ public:
 	std::map<std::string, LogicalExpression<BuildingID>> filters;
 
 	CTownInstanceConstructor();
-	CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
+	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
 	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 	void afterLoadFinalization() override;
 
@@ -87,7 +101,7 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler<CGHeroInstance
 {
 	JsonNode filtersJson;
 protected:
-	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
+	bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const override;
 	void initTypeData(const JsonNode & input) override;
 
 public:
@@ -95,7 +109,7 @@ public:
 	std::map<std::string, LogicalExpression<HeroTypeID>> filters;
 
 	CHeroInstanceConstructor();
-	CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
+	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
 	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 	void afterLoadFinalization() override;
 
@@ -115,13 +129,13 @@ class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling
 	JsonNode guards;
 
 protected:
-	bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
+	bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate> tmpl) const override;
 	void initTypeData(const JsonNode & input) override;
 
 public:
 
 	CDwellingInstanceConstructor();
-	CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
+	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
 	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 
 	bool producesCreature(const CCreature * crea) const;
@@ -207,10 +221,10 @@ public:
 
 	CBankInstanceConstructor();
 
-	CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
+	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
 	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 
-	std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override;
+	std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 85 - 51
lib/mapObjects/ObjectTemplate.cpp

@@ -53,7 +53,9 @@ ObjectTemplate::ObjectTemplate():
 	id(Obj::NO_OBJ),
 	subid(0),
 	printPriority(0),
-	stringID("")
+	width(0),
+	height(0),
+	visitable(false)
 {
 }
 
@@ -65,7 +67,13 @@ ObjectTemplate::ObjectTemplate(const ObjectTemplate& other):
 	printPriority(other.printPriority),
 	animationFile(other.animationFile),
 	editorAnimationFile(other.editorAnimationFile),
-	stringID(other.stringID)
+	stringID(other.stringID),
+	width(other.width),
+	height(other.height),
+	visitable(other.visitable),
+	blockedOffsets(other.blockedOffsets),
+	blockMapOffset(other.blockMapOffset),
+	visitableOffset(other.visitableOffset)
 {
 	//default copy constructor is failing with usedTiles this for unknown reason
 
@@ -84,11 +92,18 @@ ObjectTemplate & ObjectTemplate::operator=(const ObjectTemplate & rhs)
 	animationFile = rhs.animationFile;
 	editorAnimationFile = rhs.editorAnimationFile;
 	stringID = rhs.stringID;
+	width = rhs.width;
+	height = rhs.height;
+	visitable = rhs.visitable;
+	blockedOffsets = rhs.blockedOffsets;
+	blockMapOffset = rhs.blockMapOffset;
+	visitableOffset = rhs.visitableOffset;
 
 	usedTiles.clear();
 	usedTiles.resize(rhs.usedTiles.size());
 	for(size_t i = 0; i < usedTiles.size(); i++)
 		std::copy(rhs.usedTiles[i].begin(), rhs.usedTiles[i].end(), std::back_inserter(usedTiles[i]));
+
 	return *this;
 }
 
@@ -121,9 +136,9 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
 	assert(visitStr.size() == 6*8);
 
 	setSize(8, 6);
-	for (size_t i=0; i<6; i++) // 6 rows
+	for(size_t i = 0; i < 6; i++) // 6 rows
 	{
-		for (size_t j=0; j<8; j++) // 8 columns
+		for(size_t j = 0; j < 8; j++) // 8 columns
 		{
 			auto & tile = usedTiles[i][j];
 			tile |= VISIBLE; // assume that all tiles are visible
@@ -141,7 +156,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
 	std::string & terrStr = strings[4]; // allowed terrains, 1 = object can be placed on this terrain
 
 	assert(terrStr.size() == 9); // all terrains but rock
-	for (size_t i=0; i<9; i++)
+	for(size_t i = 0; i < 9; i++)
 	{
 		if (terrStr[8-i] == '1')
 			allowedTerrains.insert(Terrain::createTerrainTypeH3M(i));
@@ -168,6 +183,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
 		visitDir = (8|16|32|64|128);
 
 	readMsk();
+	recalculate();
 }
 
 void ObjectTemplate::readMsk()
@@ -197,9 +213,9 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
 	for(auto & byte : visitMask)
 		byte = reader.readUInt8();
 
-	for (size_t i=0; i<6; i++) // 6 rows
+	for(size_t i = 0; i < 6; i++) // 6 rows
 	{
-		for (size_t j=0; j<8; j++) // 8 columns
+		for(size_t j = 0; j < 8; j++) // 8 columns
 		{
 			auto & tile = usedTiles[5 - i][7 - j];
 			tile |= VISIBLE; // assume that all tiles are visible
@@ -213,7 +229,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
 
 	reader.readUInt16();
 	ui16 terrMask = reader.readUInt16();
-	for (size_t i=0; i<9; i++)
+	for(size_t i = 0; i < 9; i++)
 	{
 		if (((terrMask >> i) & 1 ) != 0)
 			allowedTerrains.insert(Terrain::createTerrainTypeH3M(i));
@@ -243,6 +259,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
 	readMsk();
 
 	afterLoadFixup();
+	recalculate();
 }
 
 void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
@@ -288,16 +305,16 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
 	{
 		switch (ch)
 		{
-			case ' ' : return 0;
-			case '0' : return 0;
-			case 'V' : return VISIBLE;
-			case 'B' : return VISIBLE | BLOCKED;
-			case 'H' : return BLOCKED;
-			case 'A' : return VISIBLE | BLOCKED | VISITABLE;
-			case 'T' : return BLOCKED | VISITABLE;
-			default:
-				logGlobal->error("Unrecognized char %s in template mask", ch);
-				return 0;
+		case ' ' : return 0;
+		case '0' : return 0;
+		case 'V' : return VISIBLE;
+		case 'B' : return VISIBLE | BLOCKED;
+		case 'H' : return BLOCKED;
+		case 'A' : return VISIBLE | BLOCKED | VISITABLE;
+		case 'T' : return BLOCKED | VISITABLE;
+		default:
+			logGlobal->error("Unrecognized char %s in template mask", ch);
+			return 0;
 		}
 	};
 
@@ -320,6 +337,7 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
 	printPriority = static_cast<si32>(node["zIndex"].Float());
 
 	afterLoadFixup();
+	recalculate();
 }
 
 void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const
@@ -413,38 +431,42 @@ void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const
 		node["zIndex"].Float() = printPriority;
 }
 
-ui32 ObjectTemplate::getWidth() const
+void ObjectTemplate::calculateWidth()
 {
 	//TODO: Use 2D array
-	//TODO: better precalculate and store constant value
-	ui32 ret = 0;
-	for (const auto &row : usedTiles) //copy is expensive
+	for(const auto& row : usedTiles) //copy is expensive
 	{
-		ret = std::max<ui32>(ret, (ui32)row.size());
+		width = std::max<ui32>(width, (ui32)row.size());
 	}
-	return ret;
 }
 
-ui32 ObjectTemplate::getHeight() const
+void ObjectTemplate::calculateHeight()
 {
 	//TODO: Use 2D array
-	return static_cast<ui32>(usedTiles.size());
+	height = static_cast<ui32>(usedTiles.size());
 }
 
 void ObjectTemplate::setSize(ui32 width, ui32 height)
 {
 	usedTiles.resize(height);
-	for (auto & line : usedTiles)
+	for(auto & line : usedTiles)
 		line.resize(width, 0);
 }
 
-bool ObjectTemplate::isVisitable() const
+void ObjectTemplate::calculateVsitable()
 {
-	for (auto & line : usedTiles)
-		for (auto & tile : line)
+	for(auto& line : usedTiles)
+	{
+		for(auto& tile : line)
+		{
 			if (tile & VISITABLE)
-				return true;
-	return false;
+			{
+				visitable = true;
+				return;
+			}
+		}
+	}
+	visitable = false;
 }
 
 bool ObjectTemplate::isWithin(si32 X, si32 Y) const
@@ -469,31 +491,33 @@ bool ObjectTemplate::isBlockedAt(si32 X, si32 Y) const
 	return isWithin(X, Y) && usedTiles[Y][X] & BLOCKED;
 }
 
-std::set<int3> ObjectTemplate::getBlockedOffsets() const
+void ObjectTemplate::calculateBlockedOffsets()
 {
-	std::set<int3> ret;
+	blockedOffsets.clear();
 	for(int w = 0; w < (int)getWidth(); ++w)
 	{
 		for(int h = 0; h < (int)getHeight(); ++h)
 		{
 			if (isBlockedAt(w, h))
-				ret.insert(int3(-w, -h, 0));
+				blockedOffsets.insert(int3(-w, -h, 0));
 		}
 	}
-	return ret;
 }
 
-int3 ObjectTemplate::getBlockMapOffset() const
+void ObjectTemplate::calculateBlockMapOffset()
 {
 	for(int w = 0; w < (int)getWidth(); ++w)
 	{
 		for(int h = 0; h < (int)getHeight(); ++h)
 		{
 			if (isBlockedAt(w, h))
-				return int3(w, h, 0);
+			{
+				blockMapOffset = int3(w, h, 0);
+				return;
+			}
 		}
 	}
-	return int3(0,0,0);
+	blockMapOffset = int3(0, 0, 0);
 }
 
 bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
@@ -502,6 +526,7 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
 	// 1 2 3
 	// 8   4
 	// 7 6 5
+	//TODO: static? cached?
 	int dirMap[3][3] =
 	{
 		{ visitDir &   1, visitDir &   2, visitDir &   4 },
@@ -515,26 +540,35 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
 	return dirMap[dy][dx] != 0;
 }
 
-int3 ObjectTemplate::getVisitableOffset() const
+void ObjectTemplate::calculateVisitableOffset()
 {
 	for(int y = 0; y < (int)getHeight(); y++)
-		for (int x = 0; x < (int)getWidth(); x++)
+	{
+		for(int x = 0; x < (int)getWidth(); x++)
+		{
 			if (isVisitableAt(x, y))
-				return int3(x,y,0);
-
-    //logGlobal->warn("Warning: getVisitableOffset called on non-visitable obj!");
-	return int3(0,0,0);
+			{
+				visitableOffset = int3(x, y, 0);
+				return;
+			}
+		}
+	}
+	visitableOffset = int3(0, 0, 0);
 }
 
-bool ObjectTemplate::isVisitableFromTop() const
+bool ObjectTemplate::canBePlacedAt(Terrain terrain) const
 {
-	return visitDir & 2;
-	//for some reason the line below is never called :?
-	//return isVisitableFrom (0, 1);
+	return allowedTerrains.count(terrain) != 0;
 }
 
-bool ObjectTemplate::canBePlacedAt(Terrain terrain) const
+void ObjectTemplate::recalculate()
 {
-	return allowedTerrains.count(terrain) != 0;
+	calculateWidth();
+	calculateHeight();
+	calculateVsitable();
+	//The lines below use width and height
+	calculateBlockedOffsets();
+	calculateBlockMapOffset();
+	calculateVisitableOffset();
 }
 

+ 59 - 7
lib/mapObjects/ObjectTemplate.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "../GameConstants.h"
+#include "../int3.h"
 
 class CBinaryReader;
 class CLegacyConfigParser;
@@ -50,11 +51,22 @@ public:
 	/// string ID, equals to def base name for h3m files (lower case, no extension) or specified in mod data
 	std::string stringID;
 
-	ui32 getWidth() const;
-	ui32 getHeight() const;
+	inline ui32 getWidth() const
+	{
+		return width;
+	};
+
+	inline ui32 getHeight() const
+	{ 
+		return height;
+	};
+
 	void setSize(ui32 width, ui32 height);
 
-	bool isVisitable() const;
+	inline bool isVisitable() const
+	{
+		return visitable;
+	};
 
 	// Checks object used tiles
 	// Position is relative to bottom-right corner of the object, can not be negative
@@ -62,13 +74,29 @@ public:
 	bool isVisitableAt(si32 X, si32 Y) const;
 	bool isVisibleAt(si32 X, si32 Y) const;
 	bool isBlockedAt(si32 X, si32 Y) const;
-	std::set<int3> getBlockedOffsets() const;
-	int3 getBlockMapOffset() const; //bottom-right corner when firts blocked tile is
+
+	inline std::set<int3> getBlockedOffsets() const
+	{
+		return blockedOffsets;
+	};
+
+	inline int3 getBlockMapOffset() const
+	{
+		return blockMapOffset; 
+	}; 
 
 	// Checks if object is visitable from certain direction. X and Y must be between -1..+1
 	bool isVisitableFrom(si8 X, si8 Y) const;
-	int3 getVisitableOffset() const;
-	bool isVisitableFromTop() const;
+	inline int3 getVisitableOffset() const
+	{
+		//logGlobal->warn("Warning: getVisitableOffset called on non-visitable obj!");
+		return visitableOffset;
+	};
+
+	inline bool isVisitableFromTop() const
+	{
+		return visitDir & 2;
+	};
 
 	// Checks if object can be placed on specific terrain
 	bool canBePlacedAt(Terrain terrain) const;
@@ -87,6 +115,25 @@ public:
 
 	bool operator==(const ObjectTemplate& ot) const { return (id == ot.id && subid == ot.subid); }
 
+private:
+	ui32 width;
+	ui32 height;
+	bool visitable;
+
+	std::set<int3> blockedOffsets;
+	int3 blockMapOffset;
+	int3 visitableOffset;
+
+	void recalculate();
+
+	void calculateWidth();
+	void calculateHeight();
+	void calculateVsitable();
+	void calculateBlockedOffsets();
+	void calculateBlockMapOffset();
+	void calculateVisitableOffset();
+
+public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & usedTiles;
@@ -98,6 +145,11 @@ public:
 		h & printPriority;
 		h & visitDir;
 		h & editorAnimationFile;
+
+		if (!h.saving)
+		{
+			recalculate();
+		}
 	}
 };
 

+ 1 - 1
lib/mapping/CCampaignHandler.h

@@ -146,7 +146,7 @@ public:
 	// FIXME: due to usage of JsonNode I can't make these methods const
 	const CGHeroInstance * strongestHero(PlayerColor owner);
 	std::vector<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it
-	
+
 	CCampaignScenario();
 
 	template <typename Handler> void serialize(Handler &h, const int formatVersion)

+ 1 - 1
lib/mapping/CMap.cpp

@@ -417,7 +417,7 @@ bool CMap::checkForVisitableDir(const int3 & src, const TerrainTile *pom, const
 		if(!vstd::contains(pom->blockingObjects, obj)) //this visitable object is not blocking, ignore
 			continue;
 
-		if (!obj->appearance.isVisitableFrom(src.x - dst.x, src.y - dst.y))
+		if (!obj->appearance->isVisitableFrom(src.x - dst.x, src.y - dst.y))
 			return false;
 	}
 	return true;

+ 17 - 17
lib/mapping/MapFormatH3M.cpp

@@ -958,9 +958,9 @@ void CMapLoaderH3M::readDefInfo()
 	// Read custom defs
 	for(int idd = 0; idd < defAmount; ++idd)
 	{
-		ObjectTemplate tmpl;
-		tmpl.readMap(reader);
-		templates.push_back(tmpl);
+		auto tmpl = new ObjectTemplate;
+		tmpl->readMap(reader);
+		templates.push_back(std::shared_ptr<const ObjectTemplate>(tmpl));
 	}
 }
 
@@ -977,10 +977,10 @@ void CMapLoaderH3M::readObjects()
 		int defnum = reader.readUInt32();
 		ObjectInstanceID idToBeGiven = ObjectInstanceID((si32)map->objects.size());
 
-		ObjectTemplate & objTempl = templates.at(defnum);
+		std::shared_ptr<const ObjectTemplate> objTempl = templates.at(defnum);
 		reader.skip(5);
 
-		switch(objTempl.id)
+		switch(objTempl->id)
 		{
 		case Obj::EVENT:
 			{
@@ -1212,15 +1212,15 @@ void CMapLoaderH3M::readObjects()
 
 				readMessageAndGuards(art->message, art);
 
-				if(objTempl.id == Obj::SPELL_SCROLL)
+				if(objTempl->id == Obj::SPELL_SCROLL)
 				{
 					spellID = reader.readUInt32();
 					artID = ArtifactID::SPELL_SCROLL;
 				}
-				else if(objTempl.id == Obj::ARTIFACT)
+				else if(objTempl->id == Obj::ARTIFACT)
 				{
 					//specific artifact
-					artID = objTempl.subid;
+					artID = objTempl->subid;
 				}
 
 				art->storedArtifact = CArtifactInstance::createArtifact(map, artID, spellID);
@@ -1235,7 +1235,7 @@ void CMapLoaderH3M::readObjects()
 				readMessageAndGuards(res->message, res);
 
 				res->amount = reader.readUInt32();
-				if(objTempl.subid == Res::GOLD)
+				if(objTempl->subid == Res::GOLD)
 				{
 					// Gold is multiplied by 100.
 					res->amount *= 100;
@@ -1246,7 +1246,7 @@ void CMapLoaderH3M::readObjects()
 		case Obj::RANDOM_TOWN:
 		case Obj::TOWN:
 			{
-				nobj = readTown(objTempl.subid);
+				nobj = readTown(objTempl->subid);
 				break;
 			}
 		case Obj::MINE:
@@ -1347,7 +1347,7 @@ void CMapLoaderH3M::readObjects()
 				auto dwelling = new CGDwelling();
 				nobj = dwelling;
 				CSpecObjInfo * spec = nullptr;
-				switch(objTempl.id)
+				switch(objTempl->id)
 				{
 				case Obj::RANDOM_DWELLING:
 					spec = new CCreGenLeveledCastleInfo();
@@ -1450,7 +1450,7 @@ void CMapLoaderH3M::readObjects()
 			}
 		case Obj::PYRAMID: //Pyramid of WoG object
 			{
-				if(objTempl.subid == 0)
+				if(objTempl->subid == 0)
 				{
 					nobj = new CBank();
 				}
@@ -1470,13 +1470,13 @@ void CMapLoaderH3M::readObjects()
 			}
 		default: //any other object
 			{
-				if (VLC->objtypeh->knownSubObjects(objTempl.id).count(objTempl.subid))
+				if (VLC->objtypeh->knownSubObjects(objTempl->id).count(objTempl->subid))
 				{
-					nobj = VLC->objtypeh->getHandlerFor(objTempl.id, objTempl.subid)->create(objTempl);
+					nobj = VLC->objtypeh->getHandlerFor(objTempl->id, objTempl->subid)->create(objTempl);
 				}
 				else
 				{
-					logGlobal->warn("Unrecognized object: %d:%d at %s on map %s", objTempl.id.toEnum(), objTempl.subid, objPos.toString(), map->name);
+					logGlobal->warn("Unrecognized object: %d:%d at %s on map %s", objTempl->id.toEnum(), objTempl->subid, objPos.toString(), map->name);
 					nobj = new CGObjectInstance();
 				}
 				break;
@@ -1484,11 +1484,11 @@ void CMapLoaderH3M::readObjects()
 		}
 
 		nobj->pos = objPos;
-		nobj->ID = objTempl.id;
+		nobj->ID = objTempl->id;
 		nobj->id = idToBeGiven;
 		if(nobj->ID != Obj::HERO && nobj->ID != Obj::HERO_PLACEHOLDER && nobj->ID != Obj::PRISON)
 		{
-			nobj->subID = objTempl.subid;
+			nobj->subID = objTempl->subid;
 		}
 		nobj->appearance = objTempl;
 		assert(idToBeGiven == ObjectInstanceID((si32)map->objects.size()));

+ 1 - 1
lib/mapping/MapFormatH3M.h

@@ -245,7 +245,7 @@ private:
 
 	/** List of templates loaded from the map, used on later stage to create
 	 *  objects but not needed for fully functional CMap */
-	std::vector<ObjectTemplate> templates;
+	std::vector<std::shared_ptr<const ObjectTemplate>> templates;
 
 	/** ptr to the map object which gets filled by data from the buffer */
 	CMap * map;

+ 6 - 5
lib/mapping/MapFormatJson.cpp

@@ -1110,13 +1110,14 @@ void CMapLoaderJson::MapObjectLoader::construct()
 
 	auto handler = VLC->objtypeh->getHandlerFor(typeName, subtypeName);
 
-	ObjectTemplate appearance;
+	auto appearance = new ObjectTemplate;
 
-	appearance.id = Obj(handler->type);
-	appearance.subid = handler->subtype;
-	appearance.readJson(configuration["template"], false);
+	appearance->id = Obj(handler->type);
+	appearance->subid = handler->subtype;
+	appearance->readJson(configuration["template"], false);
 
-	instance = handler->create(appearance);
+	// Will be destroyed soon and replaced with shared template
+	instance = handler->create(std::shared_ptr<const ObjectTemplate>(appearance));
 
 	instance->id = ObjectInstanceID((si32)owner->map->objects.size());
 	instance->instanceName = jsonKey;

+ 4 - 4
lib/rmg/ConnectionsPlacer.cpp

@@ -204,8 +204,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
 			auto & managerOther = *otherZone->getModificator<ObjectManager>();
 			
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
-			auto gate1 = factory->create(ObjectTemplate());
-			auto gate2 = factory->create(ObjectTemplate());
+			auto gate1 = factory->create();
+			auto gate2 = factory->create();
 			rmg::Object rmgGate1(*gate1), rmgGate2(*gate2);
 			rmgGate1.setTemplate(zone.getTerrainType());
 			rmgGate2.setTemplate(otherZone->getTerrainType());
@@ -249,8 +249,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
 	if(!success)
 	{
 		auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, generator.getNextMonlithIndex());
-		auto teleport1 = factory->create(ObjectTemplate());
-		auto teleport2 = factory->create(ObjectTemplate());
+		auto teleport1 = factory->create();
+		auto teleport2 = factory->create();
 		
 		zone.getModificator<ObjectManager>()->addRequiredObject(teleport1, connection.getGuardStrength());
 		otherZone->getModificator<ObjectManager>()->addRequiredObject(teleport2, connection.getGuardStrength());

+ 2 - 2
lib/rmg/ObjectManager.cpp

@@ -367,7 +367,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
 		objects.push_back(&instance->object());
 		if(auto * m = zone.getModificator<RoadPlacer>())
 		{
-			if(instance->object().appearance.isVisitableFromTop())
+			if(instance->object().appearance->isVisitableFromTop())
 				m->areaForRoads().add(instance->getVisitablePosition());
 			else
 			{
@@ -449,7 +449,7 @@ CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard)
 	
 	auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId);
 	
-	auto guard = (CGCreature *) guardFactory->create(ObjectTemplate());
+	auto guard = (CGCreature *) guardFactory->create();
 	guard->character = CGCreature::HOSTILE;
 	auto  hlp = new CStackInstance(creId, amount);
 	//will be set during initialization

+ 4 - 4
lib/rmg/ObstaclePlacer.cpp

@@ -31,7 +31,7 @@ void ObstaclePlacer::process()
 	
 	auto * riverManager = zone.getModificator<RiverPlacer>();
 	
-	typedef std::vector<ObjectTemplate> ObstacleVector;
+	typedef std::vector<std::shared_ptr<const ObjectTemplate>> ObstacleVector;
 	//obstacleVector possibleObstacles;
 	
 	std::map<int, ObstacleVector> obstaclesBySize;
@@ -48,8 +48,8 @@ void ObstaclePlacer::process()
 			{
 				for(auto temp : handler->getTemplates())
 				{
-					if(temp.canBePlacedAt(zone.getTerrainType()) && temp.getBlockMapOffset().valid())
-						obstaclesBySize[temp.getBlockedOffsets().size()].push_back(temp);
+					if(temp->canBePlacedAt(zone.getTerrainType()) && temp->getBlockMapOffset().valid())
+						obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp);
 				}
 			}
 		}
@@ -93,7 +93,7 @@ void ObstaclePlacer::process()
 			
 			for(auto & temp : shuffledObstacles)
 			{
-				auto handler = VLC->objtypeh->getHandlerFor(temp.id, temp.subid);
+				auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid);
 				auto obj = handler->create(temp);
 				allObjects.emplace_back(*obj);
 				rmg::Object * rmgObject = &allObjects.back();

+ 3 - 3
lib/rmg/RiverPlacer.cpp

@@ -374,10 +374,10 @@ void RiverPlacer::connectRiver(const int3 & tile)
 		auto handler = VLC->objtypeh->getHandlerFor(RIVER_DELTA_ID, RIVER_DELTA_SUBTYPE);
 		assert(handler->isStaticObject());
 		
-		std::vector<ObjectTemplate> tmplates;
+		std::vector<std::shared_ptr<const ObjectTemplate>> tmplates;
 		for(auto & temp : handler->getTemplates())
 		{
-			if(temp.canBePlacedAt(zone.getTerrainType()))
+			if(temp->canBePlacedAt(zone.getTerrainType()))
 			   tmplates.push_back(temp);
 		}
 		
@@ -389,7 +389,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
 			std::string targetTemplateName = RIVER_DELTA_TEMPLATE_NAME.at(river) + std::to_string(deltaOrientations[pos]) + ".def";
 			for(auto & templ : tmplates)
 			{
-				if(templ.animationFile == targetTemplateName)
+				if(templ->animationFile == targetTemplateName)
 				{
 					auto obj = handler->create(templ);
 					rmg::Object deltaObj(*obj, deltaPositions[pos]);

+ 3 - 3
lib/rmg/RmgObject.cpp

@@ -105,7 +105,7 @@ void Object::Instance::setPositionRaw(const int3 & position)
 
 void Object::Instance::setTemplate(const Terrain & terrain)
 {
-	if(dObject.appearance.id == Obj::NO_OBJ)
+	if(dObject.appearance->id == Obj::NO_OBJ)
 	{
 		auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
 		if(templates.empty())
@@ -130,7 +130,7 @@ void Object::Instance::clear()
 bool Object::Instance::isVisitableFrom(const int3 & position) const
 {
 	auto relPosition = position - getPosition(true);
-	return dObject.appearance.isVisitableFrom(relPosition.x, relPosition.y);
+	return dObject.appearance->isVisitableFrom(relPosition.x, relPosition.y);
 }
 
 CGObjectInstance & Object::Instance::object()
@@ -286,7 +286,7 @@ void Object::Instance::finalize(RmgMap & map)
 			throw rmgException(boost::to_string(boost::format("Tile %s of object %d at %s is outside the map") % tile.toString() % dObject.id % dObject.pos.toString()));
 	}
 	
-	if (dObject.appearance.id == Obj::NO_OBJ)
+	if (dObject.appearance->id == Obj::NO_OBJ)
 	{
 		auto terrainType = map.map().getTile(getPosition(true)).terType;
 		auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType);

+ 4 - 4
lib/rmg/TownPlacer.cpp

@@ -69,7 +69,7 @@ void TownPlacer::placeTowns(ObjectManager & manager)
 		
 		auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, zone.getTownType());
 		
-		CGTownInstance * town = (CGTownInstance *) townFactory->create(ObjectTemplate());
+		CGTownInstance * town = (CGTownInstance *) townFactory->create();
 		town->tempOwner = player;
 		town->builtBuildings.insert(BuildingID::FORT);
 		town->builtBuildings.insert(BuildingID::DEFAULT);
@@ -163,7 +163,7 @@ bool TownPlacer::placeMines(ObjectManager & manager)
 		{
 			auto mineHandler = VLC->objtypeh->getHandlerFor(Obj::MINE, res);
 			auto & rmginfo = mineHandler->getRMGInfo();
-			auto mine = (CGMine*)mineHandler->create(ObjectTemplate());
+			auto mine = (CGMine*)mineHandler->create();
 			mine->producedResource = res;
 			mine->tempOwner = PlayerColor::NEUTRAL;
 			mine->producedQuantity = mine->defaultResProduction();
@@ -184,7 +184,7 @@ bool TownPlacer::placeMines(ObjectManager & manager)
 		{
 			for(int rc = generator.rand.nextInt(1, extraRes); rc > 0; --rc)
 			{
-				auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate());
+				auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create();
 				resourse->amount = CGResource::RANDOM_AMOUNT;
 				manager.addNearbyObject(resourse, mine);
 			}
@@ -225,7 +225,7 @@ void TownPlacer::addNewTowns(int count, bool hasFort, PlayerColor player, Object
 		}
 		
 		auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType);
-		auto town = (CGTownInstance *) townFactory->create(ObjectTemplate());
+		auto town = (CGTownInstance *) townFactory->create();
 		town->ID = Obj::TOWN;
 		
 		town->tempOwner = player;

+ 17 - 17
lib/rmg/TreasurePlacer.cpp

@@ -59,11 +59,11 @@ void TreasurePlacer::addAllPossibleObjects()
 			{
 				for(auto temp : handler->getTemplates())
 				{
-					if(temp.canBePlacedAt(zone.getTerrainType()))
+					if(temp->canBePlacedAt(zone.getTerrainType()))
 					{
 						oi.generateObject = [temp]() -> CGObjectInstance *
 						{
-							return VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp);
+							return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
 						};
 						auto rmgInfo = handler->getRMGInfo();
 						oi.value = rmgInfo.value;
@@ -97,7 +97,7 @@ void TreasurePlacer::addAllPossibleObjects()
 			
 			auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, generator.rand);
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0);
-			auto obj = (CGHeroInstance *) factory->create(ObjectTemplate());
+			auto obj = (CGHeroInstance *) factory->create();
 			
 			
 			obj->subID = hid; //will be initialized later
@@ -159,7 +159,7 @@ void TreasurePlacer::addAllPossibleObjects()
 				
 				for(auto tmplate : dwellingHandler->getTemplates())
 				{
-					if(tmplate.canBePlacedAt(zone.getTerrainType()))
+					if(tmplate->canBePlacedAt(zone.getTerrainType()))
 					{
 						oi.generateObject = [tmplate, secondaryID, dwellingType]() -> CGObjectInstance *
 						{
@@ -181,7 +181,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SPELL_SCROLL, 0);
-			auto obj = (CGArtifact *) factory->create(ObjectTemplate());
+			auto obj = (CGArtifact *) factory->create();
 			std::vector<SpellID> out;
 			
 			for(auto spell : VLC->spellh->objects) //spellh size appears to be greater (?)
@@ -207,7 +207,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
+			auto obj = (CGPandoraBox *) factory->create();
 			obj->resources[Res::GOLD] = i * 5000;
 			return obj;
 		};
@@ -223,7 +223,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
+			auto obj = (CGPandoraBox *) factory->create();
 			obj->gainedExp = i * 5000;
 			return obj;
 		};
@@ -275,7 +275,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
+			auto obj = (CGPandoraBox *) factory->create();
 			auto stack = new CStackInstance(creature, creaturesAmount);
 			obj->creatures.putStack(SlotID(0), stack);
 			return obj;
@@ -292,7 +292,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
+			auto obj = (CGPandoraBox *) factory->create();
 			
 			std::vector <CSpell *> spells;
 			for(auto spell : VLC->spellh->objects)
@@ -321,7 +321,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
+			auto obj = (CGPandoraBox *) factory->create();
 			
 			std::vector <CSpell *> spells;
 			for(auto spell : VLC->spellh->objects)
@@ -349,7 +349,7 @@ void TreasurePlacer::addAllPossibleObjects()
 	oi.generateObject = [this]() -> CGObjectInstance *
 	{
 		auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-		auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
+		auto obj = (CGPandoraBox *) factory->create();
 		
 		std::vector <CSpell *> spells;
 		for(auto spell : VLC->spellh->objects)
@@ -421,7 +421,7 @@ void TreasurePlacer::addAllPossibleObjects()
 			oi.generateObject = [creature, creaturesAmount, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
 			{
 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
-				auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
+				auto obj = (CGSeerHut *) factory->create();
 				obj->rewardType = CGSeerHut::CREATURE;
 				obj->rID = creature->idNumber;
 				obj->rVal = creaturesAmount;
@@ -457,7 +457,7 @@ void TreasurePlacer::addAllPossibleObjects()
 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
 			{
 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
-				auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
+				auto obj = (CGSeerHut *) factory->create();
 				
 				obj->rewardType = CGSeerHut::EXPERIENCE;
 				obj->rID = 0; //unitialized?
@@ -481,7 +481,7 @@ void TreasurePlacer::addAllPossibleObjects()
 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
 			{
 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
-				auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
+				auto obj = (CGSeerHut *) factory->create();
 				obj->rewardType = CGSeerHut::RESOURCES;
 				obj->rID = Res::GOLD;
 				obj->rVal = generator.getConfig().questRewardValues[i];
@@ -525,7 +525,7 @@ std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo
 		if(!oi) //fail
 			break;
 		
-		if(oi->templ.isVisitableFromTop())
+		if(oi->templ->isVisitableFromTop())
 		{
 			objectInfos.push_back(oi);
 		}
@@ -599,7 +599,7 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
 			auto instanceAccessibleArea = instance.getAccessibleArea();
 			if(instance.getBlockedArea().getTilesVector().size() == 1)
 			{
-				if(instance.object().appearance.isVisitableFromTop() && instance.object().ID != Obj::CORPSE)
+				if(instance.object().appearance->isVisitableFromTop() && instance.object().ID != Obj::CORPSE)
 					instanceAccessibleArea.add(instance.getVisitablePosition());
 			}
 			
@@ -632,7 +632,7 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
 		if(oi.value > maxVal)
 			break; //this assumes values are sorted in ascending order
 		
-		if(!oi.templ.isVisitableFromTop() && !allowLargeObjects)
+		if(!oi.templ->isVisitableFromTop() && !allowLargeObjects)
 			continue;
 		
 		if(oi.value >= minValue && oi.maxPerZone > 0)

+ 1 - 1
lib/rmg/TreasurePlacer.h

@@ -19,7 +19,7 @@ class CMapGenerator;
 
 struct ObjectInfo
 {
-	ObjectTemplate templ;
+	std::shared_ptr<const ObjectTemplate> templ;
 	ui32 value = 0;
 	ui16 probability = 0;
 	ui32 maxPerZone = -1;

+ 2 - 2
lib/rmg/WaterProxy.cpp

@@ -200,7 +200,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
 		return false;
 	
 	auto subObjects = VLC->objtypeh->knownSubObjects(Obj::BOAT);
-	auto* boat = (CGBoat*)VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(subObjects, generator.rand))->create(ObjectTemplate());
+	auto* boat = (CGBoat*)VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(subObjects, generator.rand))->create();
 	
 	rmg::Object rmgObject(*boat);
 	rmgObject.setTemplate(zone.getTerrainType());
@@ -259,7 +259,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, Route
 		return false;
 	
 	int subtype = chooseRandomAppearance(generator.rand, Obj::SHIPYARD, land.getTerrainType());
-	auto shipyard = (CGShipyard*) VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create(ObjectTemplate());
+	auto shipyard = (CGShipyard*) VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create();
 	shipyard->tempOwner = PlayerColor::NEUTRAL;
 	
 	rmg::Object rmgObject(*shipyard);

+ 50 - 0
lib/serializer/BinaryDeserializer.h

@@ -382,6 +382,56 @@ public:
 			else
 			{
 				auto hlp = std::shared_ptr<NonConstT>(internalPtr);
+				data = hlp;
+				loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp);
+			}
+		}
+		else
+			data.reset();
+	}
+	template <typename T>
+	void load(std::shared_ptr<const T> &data) //version of the above for const ptr
+	{
+		typedef typename std::remove_const<T>::type NonConstT;
+		NonConstT *internalPtr;
+		load(internalPtr);
+
+		void *internalPtrDerived = typeList.castToMostDerived(internalPtr);
+
+		if(internalPtr)
+		{
+			auto itr = loadedSharedPointers.find(internalPtrDerived);
+			if(itr != loadedSharedPointers.end())
+			{
+				// This pointer is already loaded. The "data" needs to be pointed to it,
+				// so their shared state is actually shared.
+				try
+				{
+					auto actualType = typeList.getTypeInfo(internalPtr);
+					auto typeWeNeedToReturn = typeList.getTypeInfo<T>();
+					if(*actualType == *typeWeNeedToReturn)
+					{
+						// No casting needed, just unpack already stored shared_ptr and return it
+						data = boost::any_cast<std::shared_ptr<const T>>(itr->second);
+					}
+					else
+					{
+						// We need to perform series of casts
+						auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn);
+						data = boost::any_cast<std::shared_ptr<const T>>(ret);
+					}
+				}
+				catch(std::exception &e)
+				{
+					logGlobal->error(e.what());
+					logGlobal->error("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME", itr->second.type().name(), typeid(std::shared_ptr<T>).name());
+					//TODO scenario with inheritance -> we can have stored ptr to base and load ptr to derived (or vice versa)
+					throw;
+				}
+			}
+			else
+			{
+				auto hlp = std::shared_ptr<const T>(internalPtr);
 				data = hlp; //possibly adds const
 				loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp);
 			}

+ 6 - 0
lib/serializer/BinarySerializer.h

@@ -245,6 +245,12 @@ public:
 		save(internalPtr);
 	}
 	template <typename T>
+	void save(const std::shared_ptr<const T> &data)
+	{
+		const T *internalPtr = data.get();
+		save(internalPtr);
+	}
+	template <typename T>
 	void save(const std::unique_ptr<T> &data)
 	{
 		T *internalPtr = data.get();

+ 2 - 2
lib/serializer/CSerializer.h

@@ -12,8 +12,8 @@
 #include "../ConstTransitivePtr.h"
 #include "../GameConstants.h"
 
-const ui32 SERIALIZATION_VERSION = 803;
-const ui32 MINIMAL_SERIALIZATION_VERSION = 803;
+const ui32 SERIALIZATION_VERSION = 804;
+const ui32 MINIMAL_SERIALIZATION_VERSION = 804;
 const std::string SAVEGAME_MAGIC = "VCMISVG";
 
 class CHero;

+ 1 - 1
lib/spells/CSpellHandler.cpp

@@ -1017,4 +1017,4 @@ std::vector<bool> CSpellHandler::getDefaultAllowed() const
 	}
 
 	return allowedSpells;
-}
+}