فهرست منبع

Quests will now be handled as object member instead of inheritance. Enabled quest objects for AI.

DjWarmonger 13 سال پیش
والد
کامیت
554a98dbd7
7فایلهای تغییر یافته به همراه134 افزوده شده و 102 حذف شده
  1. 1 1
      AI/VCAI/VCAI.cpp
  2. 2 1
      client/UIFramework/CIntObjectClasses.cpp
  3. 60 50
      lib/CObjectHandler.cpp
  4. 23 9
      lib/CObjectHandler.h
  5. 1 0
      lib/RegisterTypes.h
  6. 34 34
      lib/map.cpp
  7. 13 7
      lib/map.h

+ 1 - 1
AI/VCAI/VCAI.cpp

@@ -3462,7 +3462,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 		case Obj::SEER_HUT:
 		case Obj::QUEST_GUARD:
 		{
-			return false; //fixme: avoid crash
+			//return false; //fixme: avoid crash
 			BOOST_FOREACH (auto q, ai->myCb->getMyQuests())
 			{
 				if (q.obj == obj)

+ 2 - 1
client/UIFramework/CIntObjectClasses.cpp

@@ -1281,7 +1281,8 @@ void CBoundedLabel::showAll(SDL_Surface * to)
 	for (int i = 0; i < lineCapacity; i++)
 	{
 		const std::string &line = lines[i];
-		if(!line.size()) continue;
+		if ( !(lines.size() && line.size())) //empty message or empty line
+			continue;
 
 		int x = pos.x;
 		if(alignment == CENTER)

+ 60 - 50
lib/CObjectHandler.cpp

@@ -4404,41 +4404,41 @@ void CQuest::getCompletionText (MetaString &iwText, std::vector<Component> &comp
 }
 void CGSeerHut::setObjToKill()
 {
-	if (missionType == MISSION_KILL_CREATURE)
+	if (quest.missionType == CQuest::MISSION_KILL_CREATURE)
 	{
-		stackToKill = getCreatureToKill(false)->getStack(0); //FIXME: stacks tend to dissapear (desync?) on server :?
-		stackToKill.count = 0; //no count in info window
-		stackDirection = checkDirection();
+		quest.stackToKill = getCreatureToKill(false)->getStack(0); //FIXME: stacks tend to dissapear (desync?) on server :?
+		quest.stackToKill.count = 0; //no count in info window
+		quest.stackDirection = checkDirection();
 	}
-	else if (missionType == MISSION_KILL_HERO)
+	else if (quest.missionType == CQuest::MISSION_KILL_HERO)
 	{
-		heroName = getHeroToKill(false)->name;
-		heroPortrait = getHeroToKill(false)->portrait;
+		quest.heroName = getHeroToKill(false)->name;
+		quest.heroPortrait = getHeroToKill(false)->portrait;
 	}
 }
 
 void CGSeerHut::initObj()
 {
 	seerName = VLC->generaltexth->seerNames[ran()%VLC->generaltexth->seerNames.size()];
-	textOption = ran()%3;
-	progress = 0;
-	if (missionType)
+	quest.textOption = ran()%3;
+	quest.progress = 0;
+	if (quest.missionType)
 	{
-		if (!isCustomFirst)
-			firstVisitText = VLC->generaltexth->quests[missionType-1][0][textOption];
-		if (!isCustomNext)
-			nextVisitText = VLC->generaltexth->quests[missionType-1][1][textOption];
-		if (!isCustomComplete)
-			completedText = VLC->generaltexth->quests[missionType-1][2][textOption];
+		if (!quest.isCustomFirst)
+			quest.firstVisitText = VLC->generaltexth->quests[quest.missionType-1][0][quest.textOption];
+		if (!quest.isCustomNext)
+			quest.nextVisitText = VLC->generaltexth->quests[quest.missionType-1][1][quest.textOption];
+		if (!quest.isCustomComplete)
+			quest.completedText = VLC->generaltexth->quests[quest.missionType-1][2][quest.textOption];
 	}
 	else
-		firstVisitText = VLC->generaltexth->seerEmpty[textOption];
+		quest.firstVisitText = VLC->generaltexth->seerEmpty[quest.textOption];
 
 }
 
 void CGSeerHut::getRolloverText (MetaString &text, bool onHover) const
 {
-	CQuest::getRolloverText (text, onHover);//TODO: simplify?
+	quest.getRolloverText (text, onHover);//TODO: simplify?
 	if (!onHover)
 		text.addReplacement(seerName);
 }
@@ -4448,7 +4448,7 @@ const std::string & CGSeerHut::getHoverText() const
 	switch (ID)
 	{
 			case 83:
-				if (progress)
+				if (quest.progress)
 				{
 					hoverName = VLC->generaltexth->allTexts[347];
 					boost::algorithm::replace_first(hoverName,"%s", seerName);
@@ -4462,7 +4462,7 @@ const std::string & CGSeerHut::getHoverText() const
 			default:
 				tlog5 << "unrecognized quest object\n";
 	}
-	if (progress & missionType) //rollover when the quest is active
+	if (quest.progress & quest.missionType) //rollover when the quest is active
 	{
 		MetaString ms;
 		getRolloverText (ms, true);
@@ -4488,9 +4488,19 @@ void CQuest::addReplacements(MetaString &out, const std::string &base) const
 	}
 }
 
+bool IQuestObject::checkQuest(const CGHeroInstance* h) const
+{
+	return quest.checkQuest(h);
+}
+
+void IQuestObject::getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h) const
+{
+	quest.getVisitText (text,components, isCustom, FirstVisit, h);
+}
+
 void CGSeerHut::getCompletionText(MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h) const
 {
-	CQuest::getCompletionText (text, components, isCustom, h);
+	quest.getCompletionText (text, components, isCustom, h);
 	switch (rewardType)
 	{
 		case 1: components.push_back(Component (Component::EXPERIENCE, 0, rVal*(100+h->getSecSkillLevel(CGHeroInstance::LEARNING)*5)/100.0, 0));
@@ -4521,16 +4531,16 @@ void CGSeerHut::setPropertyDer (ui8 what, ui32 val)
 	switch (what)
 	{
 		case 10:
-			progress = val;
+			quest.progress = val;
 			break;
 		case 11:
-			missionType = CQuest::MISSION_NONE;
+			quest.missionType = CQuest::MISSION_NONE;
 			break;
 	}
 }
 void CGSeerHut::newTurn() const
 {
-	if (lastDay >= 0 && lastDay < cb->getDate(0)) //time is up
+	if (quest.lastDay >= 0 && quest.lastDay < cb->getDate(0)) //time is up
 	{
 		cb->setObjProperty (id, 11, 0);
 		cb->setObjProperty (id, 10, 0);
@@ -4541,25 +4551,25 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 {
 	InfoWindow iw;
 	iw.player = h->getOwner();
-	if (missionType)
+	if (quest.missionType)
 	{
-		bool firstVisit = !progress;
+		bool firstVisit = !quest.progress;
 		bool failRequirements = !checkQuest(h);
 		bool isCustom=false;
 
 		if (firstVisit)
 		{
-			isCustom = isCustomFirst;
-			cb->setObjProperty (id, 10, IN_PROGRESS);
+			isCustom = quest.isCustomFirst;
+			cb->setObjProperty (id, 10, CQuest::IN_PROGRESS);
 
 			AddQuest aq;
-			aq.quest = QuestInfo (this, this, visitablePos());
+			aq.quest = QuestInfo (&quest, this, visitablePos());
 			aq.player = h->tempOwner;
 			cb->sendAndApply (&aq); //TODO: merge with setObjProperty?
 		}
 		else if (failRequirements)
 		{
-			isCustom = isCustomNext;
+			isCustom = quest.isCustomNext;
 		}
 
 		if (firstVisit || failRequirements)
@@ -4582,7 +4592,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 	}
 	else
 	{
-		iw.text << VLC->generaltexth->seerEmpty[textOption];
+		iw.text << VLC->generaltexth->seerEmpty[quest.textOption];
 		if (ID == 83)
 			iw.text.addReplacement(seerName);
 		cb->showInfoDialog(&iw);
@@ -4623,27 +4633,27 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
 {
 	if (accept)
 	{
-		switch (missionType)
+		switch (quest.missionType)
 		{
 			case CQuest::MISSION_ART:
-				for (std::vector<ui16>::const_iterator it = m5arts.begin(); it != m5arts.end(); ++it)
+				for (std::vector<ui16>::const_iterator it = quest.m5arts.begin(); it != quest.m5arts.end(); ++it)
 				{
 					cb->removeArtifact(ArtifactLocation(h, h->getArtPos(*it, false)));
 				}
 				break;
 			case CQuest::MISSION_ARMY:
-					cb->takeCreatures(h->id, m6creatures);
+					cb->takeCreatures(h->id, quest.m6creatures);
 				break;
 			case CQuest::MISSION_RESOURCES:
 				for (int i = 0; i < 7; ++i)
 				{
-					cb->giveResource(h->getOwner(), i, -m7resources[i]);
+					cb->giveResource(h->getOwner(), i, -quest.m7resources[i]);
 				}
 				break;
 			default:
 				break;
 		}
-		cb->setObjProperty (id, 10, COMPLETE); //mission complete - for AI
+		cb->setObjProperty (id, 10, CQuest::COMPLETE); //mission complete - for AI
 		cb->setObjProperty (id, 11, 0); //no more mission available - redundant?
 		completeQuest(h); //make sure to remove QuestQuard at the very end
 	}
@@ -4706,7 +4716,7 @@ void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward
 
 const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
 {
-	const CGObjectInstance *o = cb->getObjByQuestIdentifier(m13489val);
+	const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest.m13489val);
 	if(allowNull && !o)
 		return NULL;
 	assert(o && o->ID == GameConstants::HEROI_TYPE);
@@ -4715,7 +4725,7 @@ const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
 
 const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
 {
-	const CGObjectInstance *o = cb->getObjByQuestIdentifier(m13489val);
+	const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest.m13489val);
 	if(allowNull && !o)
 		return NULL;
 	assert(o && o->ID == 54);
@@ -4725,19 +4735,19 @@ const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
 void CGQuestGuard::initObj()
 {
 	blockVisit = true;
-	progress = 0;
-	textOption = ran()%3 + 3; //3-5
-	if (missionType)
+	quest.progress = 0;
+	quest.textOption = ran()%3 + 3; //3-5
+	if (quest.missionType)
 	{
-		if (!isCustomFirst)
-			firstVisitText = VLC->generaltexth->quests[missionType-1][0][textOption];
-		if (!isCustomNext)
-			nextVisitText = VLC->generaltexth->quests[missionType-1][1][textOption];
-		if (!isCustomComplete)
-			completedText = VLC->generaltexth->quests[missionType-1][2][textOption];
+		if (!quest.isCustomFirst)
+			quest.firstVisitText = VLC->generaltexth->quests[quest.missionType-1][0][quest.textOption];
+		if (!quest.isCustomNext)
+			quest.nextVisitText = VLC->generaltexth->quests[quest.missionType-1][1][quest.textOption];
+		if (!quest.isCustomComplete)
+			quest.completedText = VLC->generaltexth->quests[quest.missionType-1][2][quest.textOption];
 	}
 	else
-		firstVisitText = VLC->generaltexth->seerEmpty[textOption];
+		quest.firstVisitText = VLC->generaltexth->seerEmpty[quest.textOption];
 }
 void CGQuestGuard::completeQuest(const CGHeroInstance *h) const
 {
@@ -6361,7 +6371,7 @@ void CGBorderGuard::onHeroVisit( const CGHeroInstance * h ) const
 		cb->showInfoDialog (&iw);
 
 		AddQuest aq;
-		aq.quest = QuestInfo (this, this, visitablePos());
+		aq.quest = QuestInfo (&quest, this, visitablePos());
 		aq.player = h->tempOwner;
 		cb->sendAndApply (&aq);
 		//TODO: add this quest only once OR check for multiple instances later
@@ -6384,7 +6394,7 @@ void CGBorderGate::onHeroVisit( const CGHeroInstance * h ) const //TODO: passabi
 		cb->showInfoDialog(&iw);
 
 		AddQuest aq;
-		aq.quest = QuestInfo (this, this, visitablePos());
+		aq.quest = QuestInfo (&quest, this, visitablePos());
 		aq.player = h->tempOwner;
 		cb->sendAndApply (&aq);
 	}

+ 23 - 9
lib/CObjectHandler.h

@@ -759,7 +759,21 @@ public:
 	}
 };
 
-class DLL_LINKAGE CGSeerHut : public CArmedInstance, public CQuest //army is used when giving reward
+class DLL_LINKAGE IQuestObject
+{
+public:
+	CQuest quest;
+
+	virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const;
+	virtual bool checkQuest (const CGHeroInstance * h) const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & quest;
+	}
+};
+
+class DLL_LINKAGE CGSeerHut : public CArmedInstance, public IQuestObject //army is used when giving reward
 {
 public:
 	ui8 rewardType; //type of reward: 0 - no reward; 1 - experience; 2 - mana points; 3 - morale bonus; 4 - luck bonus; 5 - resources; 6 - main ability bonus (attak, defence etd.); 7 - secondary ability gain; 8 - artifact; 9 - spell; 10 - creature
@@ -770,21 +784,21 @@ public:
 	void initObj();
 	const std::string & getHoverText() const;
 	void setPropertyDer (ui8 what, ui32 val);
-	int checkDirection() const; //calculates the region of map where monster is placed
 	void newTurn() const;
 	void onHeroVisit (const CGHeroInstance * h) const;
-	void getRolloverText (MetaString &text, bool onHover) const;
-	void getCompletionText(MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h = NULL) const;
-	void finishQuest (const CGHeroInstance * h, ui32 accept) const; //common for both objects
-	void completeQuest (const CGHeroInstance * h) const;
 
+	int checkDirection() const; //calculates the region of map where monster is placed
 	void setObjToKill(); //remember creatures / heroes to kill after they are initialized
 	const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
 	const CGCreature *getCreatureToKill(bool allowNull = false) const;
+	void getRolloverText (MetaString &text, bool onHover) const;
+	void getCompletionText(MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h = NULL) const;
+	void finishQuest (const CGHeroInstance * h, ui32 accept) const; //common for both objects
+	void completeQuest (const CGHeroInstance * h) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<CArmedInstance&>(*this) & static_cast<CQuest&>(*this);
+		h & static_cast<CArmedInstance&>(*this) & static_cast<IQuestObject&>(*this);
 		h & rewardType & rID & rVal & seerName;
 	}
 };
@@ -1067,7 +1081,7 @@ public:
 	}
 };
 
-class DLL_LINKAGE CGBorderGuard : public CGKeys, public CQuest
+class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject
 {
 public:
 	void initObj();
@@ -1080,7 +1094,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<CQuest&>(*this);
+		h & static_cast<IQuestObject&>(*this);
 		h & static_cast<CGObjectInstance&>(*this);
 		h & blockVisit;
 	}

+ 1 - 0
lib/RegisterTypes.h

@@ -38,6 +38,7 @@ void registerTypes1(Serializer &s)
 	s.template registerType<CGCreature>();
 	s.template registerType<CGSignBottle>();
 	s.template registerType<CQuest>();
+	s.template registerType<IQuestObject>();
 	s.template registerType<CGSeerHut>();
 	s.template registerType<CGQuestGuard>();
 	s.template registerType<CGWitchHut>();

+ 34 - 34
lib/map.cpp

@@ -567,18 +567,18 @@ int Mapa::loadSeerHut( const ui8 * bufor, int i, CGObjectInstance *& nobj )
 		int artID = bufor[i]; ++i;
 		if (artID != 255) //not none quest
 		{
-			hut->m5arts.push_back (artID);
-			hut->missionType = CQuest::MISSION_ART;
+			hut->quest.m5arts.push_back (artID);
+			hut->quest.missionType = CQuest::MISSION_ART;
 		}
 		else
 		{
-			hut->missionType = CQuest::MISSION_NONE; //no mission
+			hut->quest.missionType = CQuest::MISSION_NONE; //no mission
 		}
-		hut->lastDay = -1; //no timeout
-		hut->isCustomFirst = hut->isCustomNext = hut->isCustomComplete = false;
+		hut->quest.lastDay = -1; //no timeout
+		hut->quest.isCustomFirst = hut->quest.isCustomNext = hut->quest.isCustomComplete = false;
 	}
 
-	if (hut->missionType)
+	if (hut->quest.missionType)
 	{
 		ui8 rewardType = bufor[i]; ++i;
 		hut->rewardType = rewardType;
@@ -1461,7 +1461,7 @@ void Mapa::readObjects( const ui8 * bufor, int &i)
 		case 83: //seer's hut
 			{
 				i = loadSeerHut(bufor, i, nobj);
-				addQuest (dynamic_cast<CQuest *>(nobj));
+				addQuest (dynamic_cast<IQuestObject *>(nobj));
 				break;
 			}
 		case 113: //witch hut
@@ -1722,7 +1722,7 @@ void Mapa::readObjects( const ui8 * bufor, int &i)
 				CGQuestGuard *guard = new CGQuestGuard();
 				nobj = guard;
 				loadQuest(guard, bufor, i);
-				addQuest (dynamic_cast <CQuest *>(guard));
+				addQuest (dynamic_cast <IQuestObject *>(guard));
 				break;
 			}
 		case 28: //faerie ring
@@ -1803,13 +1803,13 @@ void Mapa::readObjects( const ui8 * bufor, int &i)
 		case 9: //Border Guard
 			{
 				nobj = new CGBorderGuard();
-				addQuest (dynamic_cast<CQuest *>(nobj));
+				addQuest (dynamic_cast<IQuestObject *>(nobj));
 				break;
 			}
 		case 212: //Border Gate
 			{
 				nobj = new CGBorderGate();
-				addQuest (dynamic_cast<CQuest *>(nobj));
+				addQuest (dynamic_cast<IQuestObject *>(nobj));
 				break;
 			}
 		case 27: case 37: //Eye and Hut of Magi
@@ -1945,20 +1945,20 @@ bool Mapa::isInTheMap(const int3 &pos) const
 	else return true;
 }
 
-void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
+void Mapa::loadQuest(IQuestObject * guard, const ui8 * bufor, int & i)
 {
-	guard->missionType = bufor[i]; ++i;
+	guard->quest.missionType = bufor[i]; ++i;
 	//int len1, len2, len3;
-	switch(guard->missionType)
+	switch(guard->quest.missionType)
 	{
 	case 0:
 		return;
 	case 2:
 		{
-			guard->m2stats.resize(4);
+			guard->quest.m2stats.resize(4);
 			for(int x=0; x<4; x++)
 			{
-				guard->m2stats[x] = bufor[i++];
+				guard->quest.m2stats[x] = bufor[i++];
 			}
 		}
 		break;
@@ -1966,7 +1966,7 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
 	case 3:
 	case 4:
 		{
-			guard->m13489val = read_le_u32(bufor + i); i+=4;
+			guard->quest.m13489val = read_le_u32(bufor + i); i+=4;
 			break;
 		}
 	case 5:
@@ -1975,7 +1975,7 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
 			for(int yy=0; yy<artNumber; ++yy)
 			{
 				int artid = read_le_u16(bufor + i); i+=2;
-				guard->m5arts.push_back(artid); 
+				guard->quest.m5arts.push_back(artid); 
 				allowedArtifact[artid] = false; //these are unavailable for random generation
 			}
 			break;
@@ -1983,20 +1983,20 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
 	case 6:
 		{
 			int typeNumber = bufor[i]; ++i;
-			guard->m6creatures.resize(typeNumber);
+			guard->quest.m6creatures.resize(typeNumber);
 			for(int hh=0; hh<typeNumber; ++hh)
 			{
-				guard->m6creatures[hh].type = VLC->creh->creatures[read_le_u16(bufor + i)]; i+=2;
-				guard->m6creatures[hh].count = read_le_u16(bufor + i); i+=2;
+				guard->quest.m6creatures[hh].type = VLC->creh->creatures[read_le_u16(bufor + i)]; i+=2;
+				guard->quest.m6creatures[hh].count = read_le_u16(bufor + i); i+=2;
 			}
 			break;
 		}
 	case 7:
 		{
-			guard->m7resources.resize(7);
+			guard->quest.m7resources.resize(7);
 			for(int x=0; x<7; x++)
 			{
-				guard->m7resources[x] = read_le_u32(bufor + i); 
+				guard->quest.m7resources[x] = read_le_u32(bufor + i); 
 				i+=4;
 			}
 			break;
@@ -2004,7 +2004,7 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
 	case 8:
 	case 9:
 		{
-			guard->m13489val = bufor[i]; ++i;
+			guard->quest.m13489val = bufor[i]; ++i;
 			break;
 		}
 	}
@@ -2013,18 +2013,18 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
 	int limit = read_le_u32(bufor + i); i+=4;
 	if(limit == ((int)0xffffffff))
 	{
-		guard->lastDay = -1;
+		guard->quest.lastDay = -1;
 	}
 	else
 	{
-		guard->lastDay = limit;
+		guard->quest.lastDay = limit;
 	}
-	guard->firstVisitText = readString(bufor,i);
-	guard->nextVisitText = readString(bufor,i);
-	guard->completedText = readString(bufor,i);
-	guard->isCustomFirst = guard->firstVisitText.size() > 0;
-	guard->isCustomNext = guard->nextVisitText.size() > 0;
-	guard->isCustomComplete = guard->completedText.size() > 0;
+	guard->quest.firstVisitText = readString(bufor,i);
+	guard->quest.nextVisitText = readString(bufor,i);
+	guard->quest.completedText = readString(bufor,i);
+	guard->quest.isCustomFirst = guard->quest.firstVisitText.size() > 0;
+	guard->quest.isCustomNext = guard->quest.nextVisitText.size() > 0;
+	guard->quest.isCustomComplete = guard->quest.completedText.size() > 0;
 }
 
 TerrainTile & Mapa::getTile( const int3 & tile )
@@ -2070,10 +2070,10 @@ void Mapa::addNewArtifactInstance( CArtifactInstance *art )
 	artInstances.push_back(art);
 }
 
-void Mapa::addQuest (CQuest *quest)
+void Mapa::addQuest (IQuestObject *obj)
 {
-	quest->qid = quests.size();
-	quests.push_back(quest);
+	obj->quest.qid = quests.size();
+	quests.push_back(&obj->quest);
 }
 
 bool Mapa::loadArtifactToSlot(CGHeroInstance *h, int slot, const ui8 * bufor, int &i)

+ 13 - 7
lib/map.h

@@ -30,6 +30,7 @@ class CGCreature;
 class CQuest;
 class CGTownInstance;
 class IModableArt;
+class IQuestObject;
 
 /// Struct which describes a single terrain tile
 struct DLL_LINKAGE TerrainTile
@@ -306,7 +307,7 @@ struct DLL_LINKAGE Mapa : public CMapHeader
 	std::vector< ConstTransitivePtr<CGHeroInstance> > heroes;
 	std::vector< ConstTransitivePtr<CGTownInstance> > towns;
 	std::vector< ConstTransitivePtr<CArtifactInstance> > artInstances; //stores all artifacts
-	std::vector< ConstTransitivePtr<CQuest> > quests;
+	std::vector< ConstTransitivePtr<CQuest> > quests; //FIXME: allow to serialize quests not related to objects
 	//std::vector< ConstTransitivePtr<CCommanderInstance> > commanders;
 	//bmap<ui16, ConstTransitivePtr<CGCreature> > monsters;
 	//bmap<ui16, ConstTransitivePtr<CGHeroInstance> > heroesToBeat;
@@ -317,7 +318,7 @@ struct DLL_LINKAGE Mapa : public CMapHeader
 
 	void readEvents( const ui8 * bufor, int &i);
 	void readObjects( const ui8 * bufor, int &i);
-	void loadQuest( CQuest * guard, const ui8 * bufor, int & i);
+	void loadQuest( IQuestObject * guard, const ui8 * bufor, int & i);
 	void readDefInfo( const ui8 * bufor, int &i);
 	void readTerrain( const ui8 * bufor, int &i);
 	void readPredefinedHeroes( const ui8 * bufor, int &i);
@@ -331,7 +332,7 @@ struct DLL_LINKAGE Mapa : public CMapHeader
 
 	CArtifactInstance *createArt(int aid, int spellID = -1);
 	void addNewArtifactInstance(CArtifactInstance *art);
-	void addQuest (CQuest *quest);
+	void addQuest (IQuestObject *quest);
 	void eraseArtifactInstance(CArtifactInstance *art);
 
 
@@ -447,11 +448,16 @@ struct DLL_LINKAGE Mapa : public CMapHeader
 			for(ui32 i=0; i<objects.size(); i++)
 			{
 				if(!objects[i]) continue;
-				if(objects[i]->ID == GameConstants::HEROI_TYPE)
-					heroes.push_back(static_cast<CGHeroInstance*>(+objects[i]));
-				else if(objects[i]->ID == GameConstants::TOWNI_TYPE)
-					towns.push_back(static_cast<CGTownInstance*>(+objects[i]));
 
+				switch (objects[i]->ID)
+				{
+					case GameConstants::HEROI_TYPE:
+						heroes.push_back (static_cast<CGHeroInstance*>(+objects[i]));
+						break;
+					case GameConstants::TOWNI_TYPE:
+						towns.push_back (static_cast<CGTownInstance*>(+objects[i]));
+						break;
+				}
 				addBlockVisTiles(objects[i]); //recreate blockvis map
 			}
 			for(ui32 i=0; i<heroes.size(); i++) //if hero is visiting/garrisoned in town set appropriate pointers