Browse Source

Merge pull request #3180 from IvanSavenko/bugfixing

Fixes for miscellaneous accumulated issues
Ivan Savenko 1 year ago
parent
commit
1192dbff15

+ 3 - 0
Mods/vcmi/config/vcmi/english.json

@@ -154,6 +154,9 @@
 	"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Show heroes statistics windows}\n\nPermanently toggle on heroes statistics windows that show primary stats and spell points.",
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Skip Intro Music",
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip Intro Music}\n\nAllow actions during the intro music that plays at the beginning of each battle.",
+	
+	"vcmi.adventureMap.revisitObject.hover" : "Revisit Object",
+	"vcmi.adventureMap.revisitObject.help" : "{Revisit Object}\n\nIf a hero currently stands on a Map Object, he can revisit the location.",
 
 	"vcmi.battleWindow.pressKeyToSkipIntro" : "Press any key to start battle immediately",
 	"vcmi.battleWindow.damageEstimation.melee" : "Attack %CREATURE (%DAMAGE).",

+ 3 - 0
Mods/vcmi/config/vcmi/ukrainian.json

@@ -130,6 +130,9 @@
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Пропускати вступну музику",
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Пропускати вступну музику}\n\n Пропускати коротку музику, яка грає на початку кожної битви перед початком дії. Також можна пропустити, натиснувши клавішу ESC.",
 
+	"vcmi.adventureMap.revisitObject.hover" : "Відвідати Об'єкт",
+	"vcmi.adventureMap.revisitObject.help" : "{Відвідати Об'єкт}\n\nЯкщо герой в даний момент стоїть на об'єкті мапи, він може знову відвідати цю локацію.",
+
 	"vcmi.battleWindow.pressKeyToSkipIntro" : "Натисніть будь-яку клавішу, щоб розпочати бій",
 	"vcmi.battleWindow.damageEstimation.melee" : "Атакувати %CREATURE (%DAMAGE).",
 	"vcmi.battleWindow.damageEstimation.meleeKills" : "Атакувати %CREATURE (%DAMAGE, %KILLS).",

+ 7 - 2
client/CMT.cpp

@@ -24,6 +24,7 @@
 #include "CServerHandler.h"
 #include "ClientCommandManager.h"
 #include "windows/CMessage.h"
+#include "windows/InfoWindows.h"
 #include "render/IScreenHandler.h"
 #include "render/Graphics.h"
 
@@ -502,10 +503,14 @@ static void quitApplication()
 
 void handleQuit(bool ask)
 {
-	if(CSH->client && LOCPLINT && ask)
+	if(ask)
 	{
 		CCS->curh->set(Cursor::Map::POINTER);
-		LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], quitApplication, nullptr);
+
+		if (LOCPLINT)
+			LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], quitApplication, nullptr);
+		else
+			CInfoWindow::showYesNoDialog(CGI->generaltexth->allTexts[69], {}, quitApplication, {}, PlayerColor(1));
 	}
 	else
 	{

+ 4 - 0
client/eventsSDL/InputHandler.cpp

@@ -116,7 +116,11 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
 	if(ev.type == SDL_QUIT)
 	{
 		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
+#ifdef VCMI_ANDROID
 		handleQuit(false);
+#else
+		handleQuit(true);
+#endif
 		return;
 	}
 	else if(ev.type == SDL_KEYDOWN)

+ 11 - 3
client/lobby/OptionsTab.cpp

@@ -561,27 +561,35 @@ void OptionsTab::CPlayerOptionTooltipBox::genHeader()
 
 void OptionsTab::CPlayerOptionTooltipBox::genTownWindow()
 {
+	auto factionIndex = playerSettings.getCastleValidated();
+
+	if (playerSettings.castle == FactionID::RANDOM)
+		return genBonusWindow();
+
 	pos = Rect(0, 0, 228, 290);
 	genHeader();
 	labelAssociatedCreatures = std::make_shared<CLabel>(pos.w / 2 + 8, 122, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[79]);
-	auto factionIndex = playerSettings.getCastleValidated();
 	std::vector<std::shared_ptr<CComponent>> components;
 	const CTown * town = (*CGI->townh)[factionIndex]->town;
 
 	for(auto & elem : town->creatures)
 	{
 		if(!elem.empty())
-			components.push_back(std::make_shared<CComponent>(ComponentType::CREATURE, elem.front(), 0, CComponent::tiny));
+			components.push_back(std::make_shared<CComponent>(ComponentType::CREATURE, elem.front(), std::nullopt, CComponent::tiny));
 	}
 	boxAssociatedCreatures = std::make_shared<CComponentBox>(components, Rect(10, 140, pos.w - 20, 140));
 }
 
 void OptionsTab::CPlayerOptionTooltipBox::genHeroWindow()
 {
+	auto heroIndex = playerSettings.getHeroValidated();
+
+	if (playerSettings.hero == HeroTypeID::RANDOM)
+		return genBonusWindow();
+
 	pos = Rect(0, 0, 292, 226);
 	genHeader();
 	labelHeroSpeciality = std::make_shared<CLabel>(pos.w / 2 + 4, 117, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[78]);
-	auto heroIndex = playerSettings.getHeroValidated();
 
 	imageSpeciality = std::make_shared<CAnimImage>(AnimationPath::builtin("UN44"), (*CGI->heroh)[heroIndex]->imageIndex, 0, pos.w / 2 - 22, 134);
 	labelSpecialityName = std::make_shared<CLabel>(pos.w / 2, 188, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, (*CGI->heroh)[heroIndex]->getSpecialtyNameTranslated());

+ 3 - 3
config/objects/rewardableOncePerHero.json

@@ -249,7 +249,7 @@
 						"description" : "@core.arraytxt.202",
 						"message" : 148,
 						"appearChance" : { "max" : 34 },
-						"gainedLevels" : 1
+						"heroLevel" : 1
 					},
 					{
 						"description" : "@core.arraytxt.203",
@@ -257,7 +257,7 @@
 						"appearChance" : { "min" : 34, "max" : 67 },
 						"limiter" : { "resources" : { "gold" : 2000 } },
 						"resources" : { "gold" : -2000 },
-						"gainedLevels" : 1
+						"heroLevel" : 1
 					},
 					{
 						"description" : "@core.arraytxt.204",
@@ -265,7 +265,7 @@
 						"appearChance" : { "min" : 67 },
 						"limiter" : { "resources" : { "gems" : 10 } },
 						"resources" : { "gems" : -10 },
-						"gainedLevels" : 1
+						"heroLevel" : 1
 					},
 				]	
 			}

+ 5 - 2
lib/ArtifactUtils.cpp

@@ -255,10 +255,13 @@ DLL_LINKAGE void ArtifactUtils::insertScrrollSpellName(std::string & description
 	// However other language versions don't have name placeholder at all, so we have to be careful
 	auto nameStart = description.find_first_of('[');
 	auto nameEnd = description.find_first_of(']', nameStart);
-	if(sid.getNum() >= 0)
+
+	if(nameStart != std::string::npos && nameEnd != std::string::npos)
 	{
-		if(nameStart != std::string::npos && nameEnd != std::string::npos)
+		if(sid.getNum() >= 0)
 			description = description.replace(nameStart, nameEnd - nameStart + 1, sid.toEntity(VLC->spells())->getNameTranslated());
+		else
+			description = description.erase(nameStart, nameEnd - nameStart + 2); // erase "[spell name] " - including space
 	}
 }
 

+ 4 - 0
lib/gameState/CGameStateCampaign.cpp

@@ -217,6 +217,10 @@ void CGameStateCampaign::placeCampaignHeroes()
 
 	for(auto & heroID : heroesToRemove)
 	{
+		// Do not replace reserved heroes initially, e.g. in 1st campaign scenario in which they appear
+		if (campaignState->getHeroByType(heroID).isNull())
+			continue;
+
 		auto * hero = gameState->getUsedHero(heroID);
 		if(hero)
 		{

+ 1 - 1
lib/gameState/TavernHeroesPool.cpp

@@ -126,7 +126,7 @@ void TavernHeroesPool::onNewDay()
 			continue;
 
 		hero.second->setMovementPoints(hero.second->movementPointsLimit(true));
-		hero.second->mana = hero.second->manaLimit();
+		hero.second->mana = hero.second->getManaNewTurn();
 	}
 }
 

+ 3 - 0
lib/mapObjects/CBank.cpp

@@ -70,6 +70,9 @@ std::vector<Component> CBank::getPopupComponents(PlayerColor player) const
 	if (!VLC->settings()->getBoolean(EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION))
 		return {};
 
+	if (bc == nullptr)
+		return {};
+
 	std::map<CreatureID, int> guardsAmounts;
 	std::vector<Component> result;
 

+ 7 - 8
lib/mapObjects/CQuest.cpp

@@ -208,9 +208,9 @@ void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & com
 		addKillTargetReplacements(text);
 	}
 	
-	if(killTarget != ObjectInstanceID::NONE && stackToKill.type)
+	if(killTarget != ObjectInstanceID::NONE && stackToKill != CreatureID::NONE)
 	{
-		components.emplace_back(ComponentType::CREATURE, stackToKill.getId(), stackToKill.getCount());
+		components.emplace_back(ComponentType::CREATURE, stackToKill);
 		addKillTargetReplacements(text);
 	}
 	
@@ -314,7 +314,7 @@ void CQuest::defineQuestName()
 	if(!mission.spells.empty()) questName = CQuest::missionName(2);
 	if(!mission.secondary.empty()) questName = CQuest::missionName(2);
 	if(killTarget != ObjectInstanceID::NONE && !heroName.empty()) questName = CQuest::missionName(3);
-	if(killTarget != ObjectInstanceID::NONE && stackToKill.getType()) questName = CQuest::missionName(4);
+	if(killTarget != ObjectInstanceID::NONE && stackToKill != CreatureID::NONE) questName = CQuest::missionName(4);
 	if(!mission.artifacts.empty()) questName = CQuest::missionName(5);
 	if(!mission.creatures.empty()) questName = CQuest::missionName(6);
 	if(mission.resources.nonZero()) questName = CQuest::missionName(7);
@@ -327,9 +327,9 @@ void CQuest::addKillTargetReplacements(MetaString &out) const
 {
 	if(!heroName.empty())
 		out.replaceTextID(heroName);
-	if(stackToKill.type)
+	if(stackToKill != CreatureID::NONE)
 	{
-		out.replaceName(stackToKill);
+		out.replaceNamePlural(stackToKill);
 		out.replaceRawString(VLC->generaltexth->arraytxt[147+stackDirection]);
 	}
 }
@@ -429,9 +429,8 @@ void CGSeerHut::setObjToKill()
 	
 	if(getCreatureToKill(true))
 	{
-		quest->stackToKill = getCreatureToKill(false)->getStack(SlotID(0)); //FIXME: stacks tend to disappear (desync?) on server :?
-		assert(quest->stackToKill.type);
-		quest->stackToKill.count = 0; //no count in info window
+		quest->stackToKill = getCreatureToKill(false)->getCreature();
+		assert(quest->stackToKill != CreatureID::NONE);
 		quest->stackDirection = checkDirection();
 	}
 	else if(getHeroToKill(true))

+ 1 - 1
lib/mapObjects/CQuest.h

@@ -40,7 +40,7 @@ public:
 	// needed for messages / hover text
 	ui8 textOption;
 	ui8 completedOption;
-	CStackBasicDescriptor stackToKill;
+	CreatureID stackToKill;
 	ui8 stackDirection;
 	std::string heroName; //backup of hero name
 	HeroTypeID heroPortrait;

+ 1 - 1
lib/mapObjects/CRewardableObject.cpp

@@ -301,7 +301,7 @@ std::string CRewardableObject::getDescriptionMessage(PlayerColor player, const C
 		return configuration.description.toString();
 
 	auto rewardIndices = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
-	if (rewardIndices.empty() && !configuration.info[0].description.empty())
+	if (rewardIndices.empty() || !configuration.info[0].description.empty())
 		return configuration.info[0].description.toString();
 
 	if (!configuration.info[rewardIndices.front()].description.empty())

+ 2 - 1
lib/mapObjects/MiscObjects.cpp

@@ -779,7 +779,8 @@ std::string CGArtifact::getPopupText(PlayerColor player) const
 	if (settings["general"]["enableUiEnhancements"].Bool())
 	{
 		std::string description = VLC->artifacts()->getById(getArtifact())->getDescriptionTranslated();
-		ArtifactUtils::insertScrrollSpellName(description, SpellID::NONE); // erase text placeholder
+		if (getArtifact() == ArtifactID::SPELL_SCROLL)
+			ArtifactUtils::insertScrrollSpellName(description, SpellID::NONE); // erase text placeholder
 		return description;
 	}
 	else