Michał W. Urbańczyk 12 năm trước cách đây
mục cha
commit
9f5d1ba623

+ 48 - 23
lib/CObjectHandler.cpp

@@ -140,6 +140,11 @@ void IObjectInterface::garrisonDialogClosed(const CGHeroInstance *hero) const
 
 }
 
+void IObjectInterface::heroLevelUpDone(const CGHeroInstance *hero) const
+{
+
+}
+
 void CPlayersVisited::setPropertyDer( ui8 what, ui32 val )
 {
 	if(what == 10)
@@ -5307,6 +5312,7 @@ const std::string & CGMagicWell::getHoverText() const
 void CGPandoraBox::initObj()
 {
 	blockVisit = (ID==Obj::PANDORAS_BOX); //block only if it's really pandora's box (events also derive from that class)
+	hasGuardians = stacks.size();
 }
 
 void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
@@ -5318,11 +5324,12 @@ void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
 		cb->showBlockingDialog (&bd);
 }
 
-void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) const
+void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
 {
+	cb->removeAfterVisit(this);
+
 	InfoWindow iw;
 	iw.player = h->getOwner();
-	std::string msg = message; //in case box is removed in the meantime
 
 	bool changesPrimSkill = false;
 	for (int i = 0; i < primskills.size(); i++)
@@ -5352,14 +5359,6 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 
 		cb->showInfoDialog(&iw);
 
-		//give exp
-		if(expVal)
-			cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false);
-		//give prim skills
-		for(int i=0; i<primskills.size(); i++)
-			if(primskills[i])
-				cb->changePrimSkill(h,static_cast<PrimarySkill::PrimarySkill>(i),primskills[i],false);
-
 		//give sec skills
 		for(int i=0; i<abilities.size(); i++)
 		{
@@ -5371,8 +5370,31 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 			}
 		}
 
+		//give prim skills
+		for(int i=0; i<primskills.size(); i++)
+			if(primskills[i])
+				cb->changePrimSkill(h,static_cast<PrimarySkill::PrimarySkill>(i),primskills[i],false);
+
+		assert(!cb->isVisitCoveredByAnotherQuery(this, h));
+
+		//give exp
+		if(expVal)
+			cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false);
 	}
 
+	if(!cb->isVisitCoveredByAnotherQuery(this, h))
+		giveContentsAfterExp(h);
+	//Otherwise continuation occurs via post-level-up callback.
+}
+
+void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
+{
+	bool hadGuardians = hasGuardians; //copy, because flag will be emptied after issuing first post-battle message
+
+	std::string msg = message; //in case box is removed in the meantime
+	InfoWindow iw;
+	iw.player = h->getOwner();
+
 	if(spells.size())
 	{
 		std::set<SpellID> spellsToGive;
@@ -5404,7 +5426,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 
 	if(manaDiff)
 	{
-		getText(iw,afterBattle,manaDiff,176,177,h);
+		getText(iw,hadGuardians,manaDiff,176,177,h);
 		iw.components.push_back(Component(Component::PRIM_SKILL,5,manaDiff,0));
 		cb->showInfoDialog(&iw);
 		cb->setManaPoints(h->id, h->mana + manaDiff);
@@ -5412,7 +5434,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 
 	if(moraleDiff)
 	{
-		getText(iw,afterBattle,moraleDiff,178,179,h);
+		getText(iw,hadGuardians,moraleDiff,178,179,h);
 		iw.components.push_back(Component(Component::MORALE,0,moraleDiff,0));
 		cb->showInfoDialog(&iw);
 		GiveBonus gb;
@@ -5423,7 +5445,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 
 	if(luckDiff)
 	{
-		getText(iw,afterBattle,luckDiff,180,181,h);
+		getText(iw,hadGuardians,luckDiff,180,181,h);
 		iw.components.push_back(Component(Component::LUCK,0,luckDiff,0));
 		cb->showInfoDialog(&iw);
 		GiveBonus gb;
@@ -5441,7 +5463,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 	}
 	if(iw.components.size())
 	{
-		getText(iw,afterBattle,182,h);
+		getText(iw,hadGuardians,182,h);
 		cb->showInfoDialog(&iw);
 	}
 
@@ -5454,12 +5476,12 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 	}
 	if(iw.components.size())
 	{
-		getText(iw,afterBattle,183,h);
+		getText(iw,hadGuardians,183,h);
 		cb->showInfoDialog(&iw);
 	}
 
- 	iw.components.clear();
-// 	getText(iw,afterBattle,183,h);
+	iw.components.clear();
+	// 	getText(iw,afterBattle,183,h);
 	iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure
 	iw.text.addReplacement(h->name);
 	for(int i=0; i<artifacts.size(); i++)
@@ -5509,13 +5531,11 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 		cb->showInfoDialog(&iw);
 		cb->giveCreatures(this, h, creatures, true);
 	}
-	if(!afterBattle && msg.size())
+	if(!hasGuardians && msg.size())
 	{
 		iw.text << msg;
 		cb->showInfoDialog(&iw);
 	}
-	if (!creatures.Slots().size())
-		cb->removeObject(this); //only when we don't need to display garrison window
 }
 
 void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const
@@ -5553,7 +5573,7 @@ void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult
 	if(result.winner)
 		return;
 
-	giveContents(hero, true);
+	giveContentsUpToExp(hero);
 }
 
 void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const 
@@ -5576,11 +5596,16 @@ void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answe
 		}
 		else //if it gives something without battle
 		{
-			giveContents(hero, false);
+			giveContentsUpToExp(hero);
 		}
 	}
 }
 
+void CGPandoraBox::heroLevelUpDone(const CGHeroInstance *hero) const 
+{
+	giveContentsAfterExp(hero);
+}
+
 void CGEvent::onHeroVisit( const CGHeroInstance * h ) const
 {
 	if(!(availableFor & (1 << h->tempOwner.getNum())))
@@ -5609,7 +5634,7 @@ void CGEvent::activated( const CGHeroInstance * h ) const
 	}
 	else
 	{
-		giveContents(h,false);
+		giveContentsUpToExp(h);
 	}
 }
 

+ 6 - 2
lib/CObjectHandler.h

@@ -119,6 +119,7 @@ public:
 	virtual void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const;
 	virtual void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const;
 	virtual void garrisonDialogClosed(const CGHeroInstance *hero) const;
+	virtual void heroLevelUpDone(const CGHeroInstance *hero) const;
 
 //unified interface, AI helpers
 	virtual bool wasVisited (PlayerColor player) const;
@@ -679,6 +680,7 @@ class DLL_LINKAGE CGPandoraBox : public CArmedInstance
 {
 public:
 	std::string message;
+	bool hasGuardians; //helper - after battle even though we have no stacks, allows us to know that there was battle
 
 	//gained things:
 	ui32 gainedExp;
@@ -697,15 +699,17 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
+	void heroLevelUpDone(const CGHeroInstance *hero) const OVERRIDE;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CArmedInstance&>(*this);
-		h & message & gainedExp & manaDiff & moraleDiff & luckDiff & resources & primskills
+		h & message & hasGuardians & gainedExp & manaDiff & moraleDiff & luckDiff & resources & primskills
 			& abilities & abilityLevels & artifacts & spells & creatures;
 	}
 protected:
-	void giveContents(const CGHeroInstance *h, bool afterBattle) const;
+	void giveContentsUpToExp(const CGHeroInstance *h) const;
+	void giveContentsAfterExp(const CGHeroInstance *h) const;
 private:
 	void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const;
 	void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const;

+ 7 - 0
lib/IGameCallback.cpp

@@ -1012,3 +1012,10 @@ const CGCreature * IGameCallback::putNewMonster(CreatureID creID, int count, int
 	setObjProperty(m->id, ObjProperty::MONSTER_POWER, (si64)1000*count);
 	return dynamic_cast<const CGCreature*>(m);
 }
+
+bool IGameCallback::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero)
+{
+	//only server knows
+	assert(0);
+	return false;
+}

+ 3 - 0
lib/IGameCallback.h

@@ -274,6 +274,9 @@ public:
 	const CGObjectInstance *putNewObject(Obj ID, int subID, int3 pos);
 	const CGCreature *putNewMonster(CreatureID creID, int count, int3 pos);
 
+	//get info
+	virtual bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero);
+
 	friend struct CPack;
 	friend struct CPackForClient;
 	friend struct CPackForServer;

+ 9 - 0
server/CGameHandler.cpp

@@ -6154,6 +6154,15 @@ void CGameHandler::removeAfterVisit(const CGObjectInstance *object)
 	assert("This function needs to be called during the object visit!");
 }
 
+bool CGameHandler::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero)
+{
+	if(auto topQuery = queries.topQuery(hero->getOwner()))
+		if(auto visit = std::dynamic_pointer_cast<const CObjectVisitQuery>(topQuery))
+			return !(visit->visitedObject == obj && visit->visitingHero == hero);
+
+	return true;
+}
+
 CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat)
 {
 	heroWithDeadCommander = ObjectInstanceID();

+ 4 - 0
server/CGameHandler.h

@@ -178,6 +178,10 @@ public:
 	void giveHero(ObjectInstanceID id, PlayerColor player) OVERRIDE;
 	void changeObjPos(ObjectInstanceID objid, int3 newPos, ui8 flags) OVERRIDE;
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) OVERRIDE;
+
+
+	bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) OVERRIDE;
+
 	//////////////////////////////////////////////////////////////////////////
 	void useScholarSkill(ObjectInstanceID hero1, ObjectInstanceID hero2);
 	void setPortalDwelling(const CGTownInstance * town, bool forced, bool clear);

+ 10 - 0
server/CQuery.cpp

@@ -295,6 +295,11 @@ void CHeroLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color)
 	gh->levelUpHero(hlu.hero, hlu.skills[*answer]);
 }
 
+void CHeroLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const 
+{
+	objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
+}
+
 CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu)
 {
 	clu = Clu;
@@ -308,6 +313,11 @@ void CCommanderLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color
 	gh->levelUpCommander(clu.hero->commander, clu.skills[*answer]);
 }
 
+void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const 
+{
+	objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
+}
+
 bool CDialogQuery::endsByPlayerAnswer() const 
 {
 	return true;

+ 2 - 0
server/CQuery.h

@@ -135,6 +135,7 @@ public:
 	CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu);
 
 	virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
 
 	HeroLevelUp hlu;
 };
@@ -146,6 +147,7 @@ public:
 	CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu);
 
 	virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
 
 	CommanderLevelUp clu;
 };