Explorar o código

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

DjWarmonger %!s(int64=13) %!d(string=hai) anos
pai
achega
554a98dbd7
Modificáronse 7 ficheiros con 134 adicións e 102 borrados
  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