Pārlūkot izejas kodu

Message split for pandora box

nordsoft 2 gadi atpakaļ
vecāks
revīzija
a517e6ad8e

+ 122 - 104
lib/mapObjects/CGPandoraBox.cpp

@@ -28,7 +28,6 @@ VCMI_LIB_NAMESPACE_BEGIN
 void CGPandoraBox::init()
 {
 	blockVisit = true;
-	configuration.selectMode = Rewardable::SELECT_ALL;
 	
 	for(auto & i : configuration.info)
 		i.reward.removeObject = true;
@@ -41,92 +40,130 @@ void CGPandoraBox::initObj(CRandomGenerator & rand)
 	CRewardableObject::initObj(rand);
 }
 
-void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
+void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, bool markAsVisit) const
 {
-	auto setText = [](MetaString & text, int tId, const CGHeroInstance * h)
+	auto vi = configuration.info.at(index);
+	if(!vi.message.empty())
+	{
+		CRewardableObject::grantRewardWithMessage(h, index, markAsVisit);
+		return;
+	}
+	
+	//split reward message for pandora box
+	auto setText = [](bool cond, int posId, int negId, const CGHeroInstance * h)
 	{
-		text.appendLocalString(EMetaText::ADVOB_TXT, tId);
+		MetaString text;
+		text.appendLocalString(EMetaText::ADVOB_TXT, cond ? posId : negId);
 		text.replaceRawString(h->getNameTranslated());
+		return text;
 	};
 	
-	for(auto i : getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT))
+	auto sendInfoWindow = [h](const MetaString & text, const Rewardable::Reward & reward)
 	{
-		MetaString txt;
-		auto & r = configuration.info[i];
-		
-		if(r.reward.spells.size() == 1)
-			setText(txt, 184, h);
-		else if(!r.reward.spells.empty())
-			setText(txt, 188, h);
-		
-		if(r.reward.heroExperience || !r.reward.secondary.empty())
-			setText(txt, 175, h);
-		
-		for(int ps : r.reward.primary)
-		{
-			if(ps)
-			{
-				setText(txt, 175, h);
-				break;
-			}
-		}
-		
-		if(r.reward.manaDiff < 0)
-			setText(txt, 176, h);
-		if(r.reward.manaDiff > 0)
-			setText(txt, 177, h);
-		
-		for(auto b : r.reward.bonuses)
+		InfoWindow iw;
+		iw.player = h->tempOwner;
+		iw.text = text;
+		reward.loadComponents(iw.components, h);
+		iw.type = EInfoWindowMode::MODAL;
+		if(!iw.components.empty())
+			cb->showInfoDialog(&iw);
+	};
+
+	Rewardable::Reward temp;
+	temp.spells = vi.reward.spells;
+	temp.heroExperience = vi.reward.heroExperience;
+	temp.heroLevel = vi.reward.heroLevel;
+	temp.primary = vi.reward.primary;
+	temp.secondary = vi.reward.secondary;
+	temp.bonuses = vi.reward.bonuses;
+	temp.manaDiff = vi.reward.manaDiff;
+	temp.manaPercentage = vi.reward.manaPercentage;
+	
+	MetaString txt;
+	if(!vi.reward.spells.empty())
+		txt = setText(temp.spells.size() == 1, 184, 188, h);
+	
+	if(vi.reward.heroExperience || vi.reward.heroLevel || !vi.reward.secondary.empty())
+		txt = setText(true, 175, 175, h);
+	
+	for(int i : vi.reward.primary)
+	{
+		if(i)
 		{
-			if(b.type == BonusType::MORALE)
-			{
-				if(b.val < 0)
-					setText(txt, 178, h);
-				if(b.val > 0)
-					setText(txt, 179, h);
-			}
-			if(b.type == BonusType::LUCK)
-			{
-				if(b.val < 0)
-					setText(txt, 180, h);
-				if(b.val > 0)
-					setText(txt, 181, h);
-			}
+			txt = setText(true, 175, 175, h);
+			break;
 		}
-		
-		for(auto res : r.reward.resources)
+	}
+	
+	if(vi.reward.manaDiff || vi.reward.manaPercentage)
+		txt = setText(temp.manaDiff > 0, 177, 176, h);
+	
+	for(auto b : vi.reward.bonuses)
+	{
+		if(b.val && b.type == BonusType::MORALE)
+			txt = setText(b.val > 0, 179, 178, h);
+		if(b.val && b.type == BonusType::LUCK)
+			txt = setText(b.val > 0, 181, 180, h);
+	}
+	sendInfoWindow(txt, temp);
+	
+	//resource message
+	temp = Rewardable::Reward{};
+	temp.resources = vi.reward.resources;
+	sendInfoWindow(setText(vi.reward.resources.marketValue() > 0, 183, 182, h), temp);
+	
+	//artifacts message
+	temp = Rewardable::Reward{};
+	temp.artifacts = vi.reward.artifacts;
+	sendInfoWindow(setText(true, 183, 183, h), temp);
+	
+	//creatures message
+	temp = Rewardable::Reward{};
+	temp.creatures = vi.reward.creatures;
+	txt.clear();
+	if(!vi.reward.creatures.empty())
+	{
+		MetaString loot;
+		for(auto c : vi.reward.creatures)
 		{
-			if(res < 0)
-				setText(txt, 182, h);
-			if(res > 0)
-				setText(txt, 183, h);
+			loot.appendRawString("%s");
+			loot.replaceCreatureName(c);
 		}
 		
-		if(!r.reward.artifacts.empty())
-			setText(txt, 183, h);
-		
-		if(!r.reward.creatures.empty())
-		{
-			MetaString loot;
-			for(auto c : r.reward.creatures)
-			{
-				loot.appendRawString("%s");
-				loot.replaceCreatureName(c);
-			}
-			
-			if(r.reward.creatures.size() == 1 && r.reward.creatures[0].count == 1)
-				txt.appendLocalString(EMetaText::ADVOB_TXT, 185);
-			else
-				txt.appendLocalString(EMetaText::ADVOB_TXT, 186);
-			
-			txt.replaceRawString(loot.buildList());
-			txt.replaceRawString(h->getNameTranslated());
-		}
+		if(vi.reward.creatures.size() == 1 && vi.reward.creatures[0].count == 1)
+			txt.appendLocalString(EMetaText::ADVOB_TXT, 185);
+		else
+			txt.appendLocalString(EMetaText::ADVOB_TXT, 186);
 		
-		if(r.message.empty())
-			const_cast<MetaString&>(r.message) = txt;
+		txt.replaceRawString(loot.buildList());
+		txt.replaceRawString(h->getNameTranslated());
 	}
+	sendInfoWindow(txt, temp);
+	
+	//everything else
+	temp = vi.reward;
+	temp.heroExperience = 0;
+	temp.heroLevel = 0;
+	temp.secondary.clear();
+	temp.primary.clear();
+	temp.resources.amin(0);
+	temp.resources.amax(0);
+	temp.manaDiff = 0;
+	temp.manaPercentage = 0;
+	temp.spells.clear();
+	temp.creatures.clear();
+	temp.bonuses.clear();
+	temp.artifacts.clear();
+	sendInfoWindow(setText(true, 175, 175, h), temp);
 	
+	// grant reward afterwards. Note that it may remove object
+	if(markAsVisit)
+		markAsVisited(h);
+	grantReward(index, h);
+}
+
+void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
+{
 	BlockingDialog bd (true, false);
 	bd.player = h->getOwner();
 	bd.text.appendLocalString(EMetaText::ADVOB_TXT, 14);
@@ -171,61 +208,36 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
 	{
 		//backward compatibility
 		CCreatureSet::serializeJson(handler, "guards", 7);
-		Rewardable::VisitInfo vinfo;
+		configuration.info.emplace_back();
+		Rewardable::VisitInfo & vinfo = configuration.info.back();
 		vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
-		
-		auto addReward = [this, &vinfo](bool condition)
-		{
-			if(condition)
-			{
-				configuration.info.push_back(vinfo);
-				vinfo = Rewardable::VisitInfo{};
-				vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
-			}
-		};
 				
-		int val;
 		handler.serializeInt("experience", vinfo.reward.heroExperience, 0);
-		addReward(vinfo.reward.heroExperience);
-		
 		handler.serializeInt("mana", vinfo.reward.manaDiff, 0);
-		addReward(vinfo.reward.manaDiff);
 		
+		int val;
 		handler.serializeInt("morale", val, 0);
 		if(val)
 			vinfo.reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT, val, id);
-		addReward(val);
 		
 		handler.serializeInt("luck", val, 0);
 		if(val)
 			vinfo.reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT, val, id);
-		addReward(val);
 		
 		vinfo.reward.resources.serializeJson(handler, "resources");
-		addReward(vinfo.reward.resources.nonZero());
-		
 		{
-			bool updateReward = false;
 			auto s = handler.enterStruct("primarySkills");
 			for(int idx = 0; idx < vinfo.reward.primary.size(); idx ++)
 			{
 				handler.serializeInt(NPrimarySkill::names[idx], vinfo.reward.primary[idx], 0);
-				updateReward |= bool(vinfo.reward.primary[idx]);
 			}
-			addReward(updateReward);
 		}
 		
 		handler.serializeIdArray("artifacts", vinfo.reward.artifacts);
-		addReward(!vinfo.reward.artifacts.empty());
-		
 		handler.serializeIdArray("spells", vinfo.reward.spells);
-		addReward(!vinfo.reward.spells.empty());
-
 		handler.enterArray("creatures").serializeStruct(vinfo.reward.creatures);
-		addReward(!vinfo.reward.creatures.empty());
 		
 		{
-			bool updateReward = false;
 			auto s = handler.enterStruct("secondarySkills");
 			for(const auto & p : handler.getCurrent().Struct())
 			{
@@ -247,9 +259,7 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
 				}
 				
 				vinfo.reward.secondary[rawId] = level;
-				updateReward = true;
 			}
-			addReward(updateReward);
 		}
 	}
 }
@@ -257,10 +267,18 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
 void CGEvent::init()
 {
 	blockVisit = false;
-	configuration.selectMode = Rewardable::SELECT_ALL;
 	
 	for(auto & i : configuration.info)
+	{
 		i.reward.removeObject = removeAfterVisit;
+		if(!message.empty() && i.message.empty())
+			i.message = MetaString::createFromRawString(message);
+	}
+}
+
+void CGEvent::grantRewardWithMessage(const CGHeroInstance * contextHero, int rewardIndex, bool markAsVisit) const
+{
+	CRewardableObject::grantRewardWithMessage(contextHero, rewardIndex, markAsVisit);
 }
 
 void CGEvent::onHeroVisit( const CGHeroInstance * h ) const

+ 4 - 0
lib/mapObjects/CGPandoraBox.h

@@ -32,6 +32,8 @@ public:
 		h & message;
 	}
 protected:
+	void grantRewardWithMessage(const CGHeroInstance * contextHero, int rewardIndex, bool markAsVisit) const override;
+	
 	virtual void init();
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
@@ -55,6 +57,8 @@ public:
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
 protected:
+	void grantRewardWithMessage(const CGHeroInstance * contextHero, int rewardIndex, bool markAsVisit) const override;
+	
 	void init() override;
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 private:

+ 40 - 43
lib/mapObjects/CRewardableObject.cpp

@@ -28,44 +28,45 @@ static std::string visitedTxt(const bool visited)
 	return VLC->generaltexth->allTexts[id];
 }
 
-void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
+void CRewardableObject::grantRewardWithMessage(const CGHeroInstance * contextHero, int index, bool markAsVisit) const
 {
-	auto grantRewardWithMessage = [&](int index, bool markAsVisit) -> void
-	{
-		auto vi = configuration.info.at(index);
-		logGlobal->debug("Granting reward %d. Message says: %s", index, vi.message.toString());
- 		// show message only if it is not empty or in infobox
-		if (configuration.infoWindowType != EInfoWindowMode::MODAL || !vi.message.toString().empty())
-		{
-			InfoWindow iw;
-			iw.player = h->tempOwner;
-			iw.text = vi.message;
-			vi.reward.loadComponents(iw.components, h);
-			iw.type = configuration.infoWindowType;
-			if(!iw.components.empty() || !iw.text.toString().empty())
-				cb->showInfoDialog(&iw);
-		}
-		// grant reward afterwards. Note that it may remove object
-		if(markAsVisit)
-			markAsVisited(h);
-		grantReward(index, h);
-	};
-	auto selectRewardsMessage = [&](const std::vector<ui32> & rewards, const MetaString & dialog) -> void
+	auto vi = configuration.info.at(index);
+	logGlobal->debug("Granting reward %d. Message says: %s", index, vi.message.toString());
+	// show message only if it is not empty or in infobox
+	if (configuration.infoWindowType != EInfoWindowMode::MODAL || !vi.message.toString().empty())
 	{
-		BlockingDialog sd(configuration.canRefuse, rewards.size() > 1);
-		sd.player = h->tempOwner;
-		sd.text = dialog;
+		InfoWindow iw;
+		iw.player = contextHero->tempOwner;
+		iw.text = vi.message;
+		vi.reward.loadComponents(iw.components, contextHero);
+		iw.type = configuration.infoWindowType;
+		if(!iw.components.empty() || !iw.text.toString().empty())
+			cb->showInfoDialog(&iw);
+	}
+	// grant reward afterwards. Note that it may remove object
+	if(markAsVisit)
+		markAsVisited(contextHero);
+	grantReward(index, contextHero);
+}
 
-		if (rewards.size() > 1)
-			for (auto index : rewards)
-				sd.components.push_back(configuration.info.at(index).reward.getDisplayedComponent(h));
+void CRewardableObject::selectRewardWthMessage(const CGHeroInstance * contextHero, const std::vector<ui32> & rewardIndeces, const MetaString & dialog) const
+{
+	BlockingDialog sd(configuration.canRefuse, rewardIndeces.size() > 1);
+	sd.player = contextHero->tempOwner;
+	sd.text = dialog;
+
+	if (rewardIndeces.size() > 1)
+		for (auto index : rewardIndeces)
+			sd.components.push_back(configuration.info.at(index).reward.getDisplayedComponent(contextHero));
 
-		if (rewards.size() == 1)
-			configuration.info.at(rewards.front()).reward.loadComponents(sd.components, h);
+	if (rewardIndeces.size() == 1)
+		configuration.info.at(rewardIndeces.front()).reward.loadComponents(sd.components, contextHero);
 
-		cb->showBlockingDialog(&sd);
-	};
+	cb->showBlockingDialog(&sd);
+}
 
+void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
+{
 	if(!wasVisitedBefore(h))
 	{
 		auto rewards = getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT);
@@ -83,7 +84,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
 			{
 				auto emptyRewards = getAvailableRewards(h, Rewardable::EEventType::EVENT_NOT_AVAILABLE);
 				if (!emptyRewards.empty())
-					grantRewardWithMessage(emptyRewards[0], false);
+					grantRewardWithMessage(h, emptyRewards[0], false);
 				else
 					logMod->warn("No applicable message for visiting empty object!");
 				break;
@@ -91,26 +92,22 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
 			case 1: // one reward. Just give it with message
 			{
 				if (configuration.canRefuse)
-					selectRewardsMessage(rewards, configuration.info.at(rewards.front()).message);
+					selectRewardWthMessage(h, rewards, configuration.info.at(rewards.front()).message);
 				else
-					grantRewardWithMessage(rewards.front(), true);
+					grantRewardWithMessage(h, rewards.front(), true);
 				break;
 			}
 			default: // multiple rewards. Act according to select mode
 			{
 				switch (configuration.selectMode) {
 					case Rewardable::SELECT_PLAYER: // player must select
-						selectRewardsMessage(rewards, configuration.onSelect);
+						selectRewardWthMessage(h, rewards, configuration.onSelect);
 						break;
 					case Rewardable::SELECT_FIRST: // give first available
-						grantRewardWithMessage(rewards.front(), true);
+						grantRewardWithMessage(h, rewards.front(), true);
 						break;
 					case Rewardable::SELECT_RANDOM: // give random
-						grantRewardWithMessage(*RandomGeneratorUtil::nextItem(rewards, cb->gameState()->getRandomGenerator()), true);
-						break;
-					case Rewardable::SELECT_ALL: // give all rewards
-						for(auto i : rewards)
-							grantRewardWithMessage(i, i == rewards.size() - 1);
+						grantRewardWithMessage(h, *RandomGeneratorUtil::nextItem(rewards, cb->gameState()->getRandomGenerator()), true);
 						break;
 				}
 				break;
@@ -129,7 +126,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
 
 		auto visitedRewards = getAvailableRewards(h, Rewardable::EEventType::EVENT_ALREADY_VISITED);
 		if (!visitedRewards.empty())
-			grantRewardWithMessage(visitedRewards[0], false);
+			grantRewardWithMessage(h, visitedRewards[0], false);
 		else
 			logMod->warn("No applicable message for visiting already visited object!");
 	}

+ 3 - 0
lib/mapObjects/CRewardableObject.h

@@ -33,6 +33,9 @@ protected:
 	bool wasVisitedBefore(const CGHeroInstance * contextHero) const;
 	
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
+	
+	virtual void grantRewardWithMessage(const CGHeroInstance * contextHero, int rewardIndex, bool markAsVisit) const;
+	virtual void selectRewardWthMessage(const CGHeroInstance * contextHero, const std::vector<ui32> & rewardIndeces, const MetaString & dialog) const;
 
 public:
 	/// Visitability checks. Note that hero check includes check for hero owner (returns true if object was visited by player)

+ 1 - 2
lib/rewardable/Configuration.h

@@ -35,7 +35,6 @@ enum ESelectMode
 	SELECT_FIRST,  // first reward that matches limiters
 	SELECT_PLAYER, // player can select from all allowed rewards
 	SELECT_RANDOM, // one random reward from all mathing limiters
-	SELECT_ALL,    // provides all allowed rewards matching limiters
 };
 
 enum class EEventType
@@ -46,7 +45,7 @@ enum class EEventType
 	EVENT_NOT_AVAILABLE
 };
 
-const std::array<std::string, 4> SelectModeString{"selectFirst", "selectPlayer", "selectRandom", "selectAll"};
+const std::array<std::string, 3> SelectModeString{"selectFirst", "selectPlayer", "selectRandom"};
 const std::array<std::string, 5> VisitModeString{"unlimited", "once", "hero", "bonus", "player"};
 
 struct DLL_LINKAGE ResetInfo

+ 10 - 2
mapeditor/inspector/rewardswidget.cpp

@@ -147,12 +147,19 @@ RewardsWidget::RewardsWidget(const CMap & m, CRewardableObject & p, QWidget *par
 	{
 		ui->visitMode->setCurrentIndex(vstd::find_pos(Rewardable::VisitModeString, "once"));
 		ui->visitMode->setEnabled(false);
-		ui->selectMode->setCurrentIndex(vstd::find_pos(Rewardable::SelectModeString, "selectAll"));
+		ui->selectMode->setCurrentIndex(vstd::find_pos(Rewardable::SelectModeString, "selectFirst"));
 		ui->selectMode->setEnabled(false);
 		ui->windowMode->setEnabled(false);
 		ui->canRefuse->setEnabled(false);
 	}
 	
+	if(auto * e = dynamic_cast<CGEvent*>(&object))
+	{
+		ui->selectMode->setEnabled(true);
+		if(!e->removeAfterVisit)
+			ui->visitMode->setCurrentIndex(vstd::find_pos(Rewardable::VisitModeString, "unlimited"));
+	}
+	
 	if(dynamic_cast<CGSeerHut*>(&object))
 	{
 		ui->visitMode->setCurrentIndex(vstd::find_pos(Rewardable::VisitModeString, "once"));
@@ -512,7 +519,8 @@ void RewardsWidget::on_removeVisitInfo_clicked()
 	delete ui->visitInfoList->currentItem();
 	ui->visitInfoList->blockSignals(false);
 	on_visitInfoList_itemSelectionChanged();
-	loadCurrentVisitInfo(ui->visitInfoList->currentRow());
+	if(ui->visitInfoList->currentItem())
+		loadCurrentVisitInfo(ui->visitInfoList->currentRow());
 }
 
 void RewardsWidget::on_selectMode_currentIndexChanged(int index)