Quellcode durchsuchen

Various fixes and improvements around kill hero and kill monster quests. A few minor unrelated fixes.

Michał W. Urbańczyk vor 14 Jahren
Ursprung
Commit
ab20e024c0

+ 6 - 0
client/CBattleInterface.cpp

@@ -2386,6 +2386,12 @@ void CBattleInterface::hexLclicked(int whichOne)
 {
 	const CStack * actSt = activeStack;
 	const CStack* dest = curInt->cb->battleGetStackByPos(whichOne); //creature at destination tile; -1 if there is no one
+	if(!actSt)
+	{
+		tlog3 << "Hex l-clicked when no active stack!\n";
+		return;
+	}
+
 	if( ((whichOne%BFIELD_WIDTH)!=0 && (whichOne%BFIELD_WIDTH)!=(BFIELD_WIDTH-1)) //if player is trying to attack enemey unit or move creature stack
 		|| (actSt->hasBonusOfType(Bonus::CATAPULT) && !spellDestSelectMode || dest ) //enemy's first aid tent can stand there and we want to shoot it
 		)

+ 1 - 1
lib/BattleState.cpp

@@ -850,7 +850,7 @@ ui32 BattleInfo::calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const C
 		else if(sp->earth)
 			ret *= (100.0f + caster->valOfBonuses(Bonus::EARTH_SPELL_DMG_PREMY)) / 100.0f;
 
-		if (affectedCreature) //Hero specials like Solmyr, Deemer
+		if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
 			ret *= (100.f + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, sp->id) * caster->level) / affectedCreature->getCreature()->level)) / 100.0f;
 	}
 	return ret;

+ 1 - 0
lib/CCreatureHandler.cpp

@@ -486,6 +486,7 @@ void CCreatureHandler::loadCreatures()
 			if(!ifs.good())
 				break;
 			CCreature *c = creatures[id];
+			c->level = lvl;
 			if(isbetw(lvl, 0, ARRAY_COUNT(creaturesOfLevel)))
 				c->attachTo(&creaturesOfLevel[lvl]);
 			else

+ 1 - 1
lib/CGameState.cpp

@@ -337,7 +337,7 @@ void MetaString::addReplacement(const CStackBasicDescriptor &stack)
 {
 	assert(stack.count); //valid count
 	assert(stack.type); //valid type
-	addReplacement(stack.type->idNumber, stack.count);
+	addCreReplacement(stack.type->idNumber, stack.count);
 }
 
 static CGObjectInstance * createObject(int id, int subid, int3 pos, int owner)

+ 92 - 75
lib/CObjectHandler.cpp

@@ -1116,6 +1116,7 @@ void CGHeroInstance::initObj()
 	UpdateSpeciality();
 
 	mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
+	type->name = name;
 }
 void CGHeroInstance::UpdateSpeciality()
 {
@@ -3871,12 +3872,10 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
 	{
 		case MISSION_NONE:
 			return true;
-			break;
 		case MISSION_LEVEL:
 			if (m13489val <= h->level)
 				return true;
 			return false;
-			break;
 		case MISSION_PRIMARY_STAT:
 			for (int i = 0; i < 4; ++i)
 			{
@@ -3884,17 +3883,11 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
 					return false;
 			}
 			return true;
-			break;
 		case MISSION_KILL_HERO:
-			if (h->cb->gameState()->map->heroesToBeat[m13489val]->tempOwner < PLAYER_LIMIT)
-				return false; //if the pointer is not NULL
-			return true;
-			break;
 		case MISSION_KILL_CREATURE:
-			if (h->cb->gameState()->map->monsters[m13489val]->pos == int3(-1,-1,-1))
+			if (!h->cb->getObjByQuestIdentifier(m13489val))
 				return true;
 			return false;
-			break;
 		case MISSION_ART:
 			for (int i = 0; i < m5arts.size(); ++i)
 			{
@@ -3903,7 +3896,6 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
 				return false; //if the artifact was not found
 			}
 			return true;
-			break;
 		case MISSION_ARMY:
 			{
 				std::vector<CStackBasicDescriptor>::const_iterator cre;
@@ -3921,7 +3913,6 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
 				}
 			}
 			return true;
-			break;
 		case MISSION_RESOURCES:
 			for (int i = 0; i < 7; ++i) //including Mithril ?
 			{	//Quest has no direct access to callback
@@ -3929,17 +3920,14 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
 					return false;
 			}
 			return true;
-			break;
 		case MISSION_HERO:
 			if (m13489val == h->type->ID)
 				return true;
 			return false;
-			break;
 		case MISSION_PLAYER:
 			if (m13489val == h->getOwner())
 				return true;
 			return false;
-			break;
 		default:
 			return false;
 	}
@@ -3958,6 +3946,17 @@ void CGSeerHut::initObj()
 			nextVisitText = VLC->generaltexth->quests[missionType-1][1][textOption];
 			completedText = VLC->generaltexth->quests[missionType-1][2][textOption];
 		}
+
+		if(missionType == MISSION_KILL_CREATURE)
+		{
+			stackToKill = getCreatureToKill(false)->getStack(0);
+			stackDirection = checkDirection();
+		}
+		else if(missionType == MISSION_KILL_HERO)
+		{
+			heroName = getHeroToKill(false)->name;
+			heroPortrait = getHeroToKill(false)->portrait;
+		}
 	}
 	else
 		firstVisitText = VLC->generaltexth->seerEmpty[textOption];
@@ -3989,30 +3988,25 @@ const std::string & CGSeerHut::getHoverText() const
 				ms.addReplacement(m13489val);
 				break;
 			case MISSION_PRIMARY_STAT:
-			{
-				MetaString loot;
-				for (int i = 0; i < 4; ++i)
 				{
-					if (m2stats[i])
+					MetaString loot;
+					for (int i = 0; i < 4; ++i)
 					{
-						loot << "%d %s";
-						loot.addReplacement(m2stats[i]);
-						loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
-					}		
+						if (m2stats[i])
+						{
+							loot << "%d %s";
+							loot.addReplacement(m2stats[i]);
+							loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
+						}		
+					}
+					ms.addReplacement(loot.buildList());
 				}
-				ms.addReplacement(loot.buildList());
-			}
 				break;
 			case MISSION_KILL_HERO:
-				ms.addReplacement(cb->gameState()->map->heroesToBeat[m13489val]->name);
-				break;
-			case MISSION_HERO:
-				ms.addReplacement(VLC->heroh->heroes[m13489val]->name);
+				ms.addReplacement(heroName);
 				break;
 			case MISSION_KILL_CREATURE:
-				{
-					ms.addReplacement(cb->gameState()->map->monsters[m13489val]->getStack(0));
-				}
+				ms.addReplacement(stackToKill);
 				break;
 			case MISSION_ART:
 				{
@@ -4051,6 +4045,9 @@ const std::string & CGSeerHut::getHoverText() const
 					ms.addReplacement(loot.buildList());
 				}
 				break;
+			case MISSION_HERO:
+				ms.addReplacement(VLC->heroh->heroes[m13489val]->name);
+				break;
 			case MISSION_PLAYER:
 				ms.addReplacement(VLC->generaltexth->colors[m13489val]);
 				break;
@@ -4096,7 +4093,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 			switch (missionType)
 			{
 				case MISSION_LEVEL:
-					iw.components.push_back (Component (Component::EXPERIENCE, 1, m13489val, 0));
+					iw.components.push_back(Component (Component::EXPERIENCE, 1, m13489val, 0));
 					if (!isCustom)
 						iw.text.addReplacement(m13489val);
 					break;
@@ -4107,7 +4104,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 					{
 						if (m2stats[i])
 						{
-							iw.components.push_back (Component (Component::PRIM_SKILL, i, m2stats[i], 0));
+							iw.components.push_back(Component (Component::PRIM_SKILL, i, m2stats[i], 0));
 							loot << "%d %s";
 							loot.addReplacement(m2stats[i]);
 							loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
@@ -4118,28 +4115,20 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 				}
 					break;
 				case MISSION_KILL_HERO:
-					iw.components.push_back (Component (Component::HERO,
-						cb->gameState()->map->heroesToBeat[m13489val]->type->ID, 0, 0));
+					iw.components.push_back(Component(Component::HERO, heroPortrait, 0, 0));
 					if (!isCustom)
-						iw.text.addReplacement(cb->gameState()->map->heroesToBeat[m13489val]->name);
+						addReplacements(iw.text, firstVisitText);
 					break;
 				case MISSION_HERO:
-					iw.components.push_back (Component (Component::HERO, m13489val, 0, 0));
+					iw.components.push_back(Component (Component::HERO, m13489val, 0, 0));
 					if (!isCustom)
 						iw.text.addReplacement(VLC->heroh->heroes[m13489val]->name);
 					break;
 				case MISSION_KILL_CREATURE:
 					{
-						CStackInstance stack = cb->gameState()->map->monsters[m13489val]->getStack(0);
-						iw.components.push_back (Component(stack));
+						iw.components.push_back(Component(stackToKill));
 						if (!isCustom)
-						{
-							iw.text.addReplacement(stack);
-							if (std::count(firstVisitText.begin(), firstVisitText.end(), '%') == 2) //say where is placed monster
-							{
-								iw.text.addReplacement(VLC->generaltexth->arraytxt[147+checkDirection()]);
-							}
-						}
+							addReplacements(iw.text, firstVisitText);
 					}
 					break;
 				case MISSION_ART:
@@ -4147,12 +4136,12 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 					MetaString loot;
 					for (std::vector<ui16>::const_iterator it = m5arts.begin(); it != m5arts.end(); ++it)
 					{
-						iw.components.push_back (Component (Component::ARTIFACT, *it, 0, 0));
+						iw.components.push_back(Component (Component::ARTIFACT, *it, 0, 0));
 						loot << "%s";
 						loot.addReplacement(MetaString::ART_NAMES, *it);
 					}
 					if (!isCustom)
-						iw.text.addReplacement	(loot.buildList());
+						iw.text.addReplacement(loot.buildList());
 				}
 					break;
 				case MISSION_ARMY:
@@ -4165,7 +4154,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 						loot.addReplacement(*it);
 					}
 					if (!isCustom)
-						iw.text.addReplacement	(loot.buildList());
+						iw.text.addReplacement(loot.buildList());
 				}
 					break;
 				case MISSION_RESOURCES:
@@ -4175,20 +4164,20 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 					{
 						if (m7resources[i])
 						{
-							iw.components.push_back (Component (Component::RESOURCE, i, m7resources[i], 0));
+							iw.components.push_back(Component (Component::RESOURCE, i, m7resources[i], 0));
 							loot << "%d %s";
 							loot.addReplacement(m7resources[i]);
 							loot.addReplacement(MetaString::RES_NAMES, i);
 						}
 					}
 					if (!isCustom)
-						iw.text.addReplacement	(loot.buildList());
+						iw.text.addReplacement(loot.buildList());
 				}
 					break;
 				case MISSION_PLAYER:
-					iw.components.push_back (Component (Component::FLAG, m13489val, 0, 0));
+					iw.components.push_back(Component (Component::FLAG, m13489val, 0, 0));
 					if (!isCustom)
-						iw.text.addReplacement	(VLC->generaltexth->colors[m13489val]);
+						iw.text.addReplacement(VLC->generaltexth->colors[m13489val]);
 					break;
 			}
 			cb->setObjProperty (id,10,1);
@@ -4197,6 +4186,8 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 		else if (!checkQuest(h))
 		{
 			iw.text << nextVisitText;
+			if(!isCustom)
+				addReplacements(iw.text, nextVisitText);
 			cb->showInfoDialog(&iw);
 		}
 		if (checkQuest(h)) // propose completion, also on first visit
@@ -4269,23 +4260,14 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 				}
 					break;
 				case MISSION_KILL_HERO:
+				case MISSION_KILL_CREATURE:
 					if (!isCustom)
-						bd.text.addReplacement(cb->gameState()->map->heroesToBeat[m13489val]->name);
+						addReplacements(bd.text, completedText);
 					break;
 				case MISSION_HERO:
 					if (!isCustom)
 						bd.text.addReplacement(VLC->heroh->heroes[m13489val]->name);
 					break;
-				case MISSION_KILL_CREATURE:
-				{
-					{
-						bd.text.addReplacement(cb->gameState()->map->monsters[m13489val]->getArmy()[0]);
-						if (std::count(firstVisitText.begin(), firstVisitText.end(), '%') == 2) //say where is placed monster
-						{
-							bd.text.addReplacement(VLC->generaltexth->arraytxt[147+checkDirection()]);
-						}
-					}
-				}
 				case MISSION_PLAYER:
 					if (!isCustom)
 						bd.text.addReplacement(VLC->generaltexth->colors[m13489val]);
@@ -4294,25 +4276,25 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 			
 			switch (rewardType)
 			{
-				case 1: bd.components.push_back (Component (Component::EXPERIENCE, 0, rVal*(100+h->getSecSkillLevel(CGHeroInstance::LEARNING)*5)/100.0f, 0));
+				case 1: bd.components.push_back(Component (Component::EXPERIENCE, 0, rVal*(100+h->getSecSkillLevel(CGHeroInstance::LEARNING)*5)/100.0f, 0));
 					break;
-				case 2: bd.components.push_back (Component (Component::PRIM_SKILL, 5, rVal, 0));
+				case 2: bd.components.push_back(Component (Component::PRIM_SKILL, 5, rVal, 0));
 					break;
-				case 3: bd.components.push_back (Component (Component::MORALE, 0, rVal, 0));
+				case 3: bd.components.push_back(Component (Component::MORALE, 0, rVal, 0));
 					break;
-				case 4: bd.components.push_back (Component (Component::LUCK, 0, rVal, 0));
+				case 4: bd.components.push_back(Component (Component::LUCK, 0, rVal, 0));
 					break;
-				case 5: bd.components.push_back (Component (Component::RESOURCE, rID, rVal, 0));
+				case 5: bd.components.push_back(Component (Component::RESOURCE, rID, rVal, 0));
 					break;
-				case 6: bd.components.push_back (Component (Component::PRIM_SKILL, rID, rVal, 0));
+				case 6: bd.components.push_back(Component (Component::PRIM_SKILL, rID, rVal, 0));
 					break;
-				case 7: bd.components.push_back (Component (Component::SEC_SKILL, rID, rVal, 0));
+				case 7: bd.components.push_back(Component (Component::SEC_SKILL, rID, rVal, 0));
 					break;
-				case 8: bd.components.push_back (Component (Component::ARTIFACT, rID, 0, 0));
+				case 8: bd.components.push_back(Component (Component::ARTIFACT, rID, 0, 0));
 					break;
-				case 9: bd.components.push_back (Component (Component::SPELL, rID, 0, 0));
+				case 9: bd.components.push_back(Component (Component::SPELL, rID, 0, 0));
 					break;
-				case 10: bd.components.push_back (Component (Component::CREATURE, rID, rVal, 0));
+				case 10: bd.components.push_back(Component (Component::CREATURE, rID, rVal, 0));
 					break;
 			}
 			
@@ -4330,7 +4312,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 }
 int CGSeerHut::checkDirection() const
 {
-	int3 cord = cb->gameState()->map->monsters[m13489val]->pos;
+	int3 cord = getCreatureToKill()->pos;
 	if ((double)cord.x/(double)cb->getMapSize().x < 0.34) //north
 	{
 		if ((double)cord.y/(double)cb->getMapSize().y < 0.34) //northwest
@@ -4443,6 +4425,41 @@ void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward
 	}
 }
 
+const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
+{
+	const CGObjectInstance *o = cb->getObjByQuestIdentifier(m13489val);
+	if(allowNull && !o)
+		return NULL;
+	assert(o && o->ID == HEROI_TYPE);
+	return static_cast<const CGHeroInstance*>(o);
+}
+
+const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
+{
+	const CGObjectInstance *o = cb->getObjByQuestIdentifier(m13489val);
+	if(allowNull && !o)
+		return NULL;
+	assert(o && o->ID == 54);
+	return static_cast<const CGCreature*>(o);
+}
+
+void CGSeerHut::addReplacements(MetaString &out, const std::string &base) const
+{
+	switch(missionType)
+	{
+	case MISSION_KILL_CREATURE:
+		out.addReplacement(stackToKill);
+		if (std::count(base.begin(), base.end(), '%') == 2) //say where is placed monster
+		{
+			out.addReplacement(VLC->generaltexth->arraytxt[147+stackDirection]);
+		}
+		break;
+	case MISSION_KILL_HERO:
+		out.addReplacement(heroName);
+		break;
+	}
+}
+
 void CGQuestGuard::initObj()
 {
 	blockVisit = true;

+ 15 - 1
lib/CObjectHandler.h

@@ -309,7 +309,7 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 
-	const CHero * type;
+	ConstTransitivePtr<CHero> type;
 	ui64 exp; //experience points
 	si32 level; //current level of hero
 	std::string name; //may be custom
@@ -760,6 +760,14 @@ public:
 	ui8 textOption; //store randomized mission write-ups rather than entire string (?)
 	std::string seerName;
 
+	//following field are used only for kill creature/hero missions, the original objects became inaccessible after their removal, so we need to store info needed for messages / hover text
+	//TODO? organize
+	CStackBasicDescriptor stackToKill; 
+	ui8 stackDirection;
+	std::string heroName; //backup of hero name
+	si32 heroPortrait;
+
+
 	void initObj();
 	const std::string & getHoverText() const;
 	void setPropertyDer (ui8 what, ui32 val);
@@ -769,10 +777,16 @@ public:
 	void finishQuest (const CGHeroInstance * h, ui32 accept) const; //common for both objects
 	void completeQuest (const CGHeroInstance * h) const;
 
+	const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
+	const CGCreature *getCreatureToKill(bool allowNull = false) const;
+
+	void addReplacements(MetaString &out, const std::string &base) const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGObjectInstance&>(*this) & static_cast<CQuest&>(*this);
 		h & rewardType & rID & rVal & textOption & seerName;
+		h & stackToKill & stackDirection & heroName & heroPortrait;
 	}
 };
 

+ 6 - 0
lib/IGameCallback.cpp

@@ -266,4 +266,10 @@ const PlayerState * IGameCallback::getPlayerState( int color )
 const CTown * IGameCallback::getNativeTown(int color)
 {
 	return &VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(color).castle];
+}
+
+const CGObjectInstance * IGameCallback::getObjByQuestIdentifier(int identifier)
+{
+	assert(vstd::contains(gs->map->questIdentifierToId, identifier));
+	return getObj(gs->map->questIdentifierToId[identifier]);
 }

+ 1 - 0
lib/IGameCallback.h

@@ -56,6 +56,7 @@ public:
 	virtual const CGHeroInstance* getHero(int objid);
 	virtual const CGTownInstance* getTown(int objid);
 	virtual const CGHeroInstance* getSelectedHero(int player); //NULL if no hero is selected
+	virtual const CGObjectInstance *getObjByQuestIdentifier(int identifier); //NULL if object has been removed (eg. killed)
 	virtual int getCurrentPlayer()=0;
 	virtual int getSelectedHero()=0;
 	virtual const PlayerSettings * getPlayerSettings(int color);

+ 10 - 10
lib/NetPacksLib.cpp

@@ -243,11 +243,8 @@ DLL_EXPORT void RemoveObject::applyGs( CGameState *gs )
 	if(obj->ID==HEROI_TYPE)
 	{
 		CGHeroInstance *h = static_cast<CGHeroInstance*>(obj);
-		std::vector<ConstTransitivePtr<CGHeroInstance> >::iterator nitr = std::find(gs->map->heroes.begin(), gs->map->heroes.end(),h);
-		gs->map->heroes.erase(nitr);
-		int player = h->tempOwner;
-		nitr = std::find(gs->getPlayer(player)->heroes.begin(), gs->getPlayer(player)->heroes.end(), h);
-		gs->getPlayer(player)->heroes.erase(nitr);
+		gs->map->heroes -= h;
+		gs->getPlayer(h->tempOwner)->heroes -= h;
 		h->tempOwner = 255; //no one owns beaten hero
 
 		if(CGTownInstance *t = const_cast<CGTownInstance *>(h->visitedTown))
@@ -264,13 +261,16 @@ DLL_EXPORT void RemoveObject::applyGs( CGameState *gs )
 		if(!vstd::contains(gs->hpool.pavailable, h->subID))
 			gs->hpool.pavailable[h->subID] = 0xff;
 
+		gs->map->objects[id] = NULL;
+		
+
 		return;
 	}
-	else if (obj->ID==CREI_TYPE  &&  gs->map->version > CMapHeader::RoE) //only fixed monsters can be a part of quest
-	{
-		CGCreature *cre = static_cast<CGCreature*>(obj);
-		gs->map->monsters[cre->identifier]->pos = int3 (-1,-1,-1);	//use nonexistent monster for quest :>
-	}
+// 	else if (obj->ID==CREI_TYPE  &&  gs->map->version > CMapHeader::RoE) //only fixed monsters can be a part of quest
+// 	{
+// 		CGCreature *cre = static_cast<CGCreature*>(obj);
+// 		gs->map->monsters[cre->identifier]->pos = int3 (-1,-1,-1);	//use nonexistent monster for quest :>
+// 	}
 	gs->map->objects[id].dellNull();
 }
 

+ 9 - 6
lib/map.cpp

@@ -779,14 +779,15 @@ void Mapa::loadTown( CGObjectInstance * &nobj, const unsigned char * bufor, int
 	nt->garrisonHero = NULL;
 }
 
-CGObjectInstance * Mapa::loadHero(const unsigned char * bufor, int &i)
+CGObjectInstance * Mapa::loadHero(const unsigned char * bufor, int &i, int idToBeGiven)
 {
 	CGHeroInstance * nhi = new CGHeroInstance();
 
 	int identifier = 0;
-	if(version>RoE)
+	if(version > RoE)
 	{
 		identifier = readNormalNr(bufor,i, 4); i+=4;
+		questIdentifierToId[identifier] = idToBeGiven;
 	}
 
 	ui8 owner = bufor[i++];
@@ -815,7 +816,6 @@ CGObjectInstance * Mapa::loadHero(const unsigned char * bufor, int &i)
 			break;
 		}
 	}
-	heroesToBeat[identifier] = nhi;
 	if(readChar(bufor,i))//true if hero has nonstandard name
 		nhi->name = readString(bufor,i);
 	if(version>AB)
@@ -1224,6 +1224,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 		pos.z = bufor[i++];
 
 		int defnum = readNormalNr(bufor,i, 4); i+=4;
+		int idToBeGiven = objects.size();
 
 		CGDefInfo * defInfo = defy[defnum];
 		i+=5;
@@ -1306,7 +1307,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 			}
 		case 34: case 70: case 62: //34 - hero; 70 - random hero; 62 - prison
 			{
-				nobj = loadHero(bufor, i);
+				nobj = loadHero(bufor, i, idToBeGiven);
 				break;
 			}
 		case 4: //Arena
@@ -1358,7 +1359,8 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 				if(version>RoE)
 				{
 					cre->identifier = readNormalNr(bufor,i); i+=4;
-					monsters[cre->identifier] = cre;
+					questIdentifierToId[cre->identifier] = idToBeGiven;
+					//monsters[cre->identifier] = cre;
 				}
 
 				CStackInstance *hlp = new CStackInstance();
@@ -1846,10 +1848,11 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 
 		nobj->pos = pos;
 		nobj->ID = defInfo->id;
-		nobj->id = objects.size();
+		nobj->id = idToBeGiven;
 		if(nobj->ID != HEROI_TYPE && nobj->ID != 214 && nobj->ID != 62)
 			nobj->subID = defInfo->subid;
 		nobj->defInfo = defInfo;
+		assert(idToBeGiven == objects.size());
 		objects.push_back(nobj);
 		if(nobj->ID==TOWNI_TYPE)
 			towns.push_back(static_cast<CGTownInstance*>(nobj));

+ 6 - 5
lib/map.h

@@ -287,8 +287,10 @@ struct DLL_EXPORT Mapa : public CMapHeader
 	std::vector< ConstTransitivePtr<CGHeroInstance> > heroes;
 	std::vector< ConstTransitivePtr<CGTownInstance> > towns;
 	std::vector< ConstTransitivePtr<CArtifactInstance> > artInstances; //stores all artifacts
-	bmap<ui16, ConstTransitivePtr<CGCreature> > monsters;
-	bmap<ui16, ConstTransitivePtr<CGHeroInstance> > heroesToBeat;
+	//bmap<ui16, ConstTransitivePtr<CGCreature> > monsters;
+	//bmap<ui16, ConstTransitivePtr<CGHeroInstance> > heroesToBeat;
+
+	bmap<si32, si32> questIdentifierToId;
 
 	void initFromBytes( const unsigned char * bufor); //creates map from decompressed .h3m data
 
@@ -300,7 +302,7 @@ struct DLL_EXPORT Mapa : public CMapHeader
 	void readPredefinedHeroes( const unsigned char * bufor, int &i);
 	void readHeader( const unsigned char * bufor, int &i);
 	void readRumors( const unsigned char * bufor, int &i);
-	CGObjectInstance *loadHero(const unsigned char * bufor, int &i);
+	CGObjectInstance *loadHero(const unsigned char * bufor, int &i, int idToBeGiven);
 	void loadArtifactsOfHero(const unsigned char * bufor, int & i, CGHeroInstance * nhi);
 	bool loadArtifactToSlot(CGHeroInstance *h, int slot, const unsigned char * bufor, int &i);
 	void loadTown( CGObjectInstance * &nobj, const unsigned char * bufor, int &i, int subid);
@@ -326,9 +328,8 @@ struct DLL_EXPORT Mapa : public CMapHeader
 	{
 		h & static_cast<CMapHeader&>(*this);
 		h & rumors & allowedSpell & allowedAbilities & allowedArtifact & allowedHeroes & events & grailPos;
-		h & monsters;
-		h & heroesToBeat;
 		h & artInstances; //hopefully serialization is now automagical?
+		h & questIdentifierToId;
 
 		//TODO: viccondetails
 		if(h.saving)

+ 1 - 1
server/CGameHandler.cpp

@@ -1529,7 +1529,7 @@ void CGameHandler::giveCreatures(const CArmedInstance *obj, const CGHeroInstance
 	COMPLAIN_RET_IF(creatures.stacksCount() > ARMY_SIZE, "Too many stacks to give!");
 
 	//first we move creatures to give to make them army of object-source
-	for(int i = 0; creatures.stacksCount(); i++)
+	for(int i = 0; i != creatures.stacksCount(); i++)
 	{
 		TSlots::const_iterator stack = creatures.Slots().begin();
 		addToSlot(StackLocation(obj, i), stack->second->type, stack->second->count);