From 6625831cf6d8f9fe9453775677d269fa64f125ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Tue, 31 Oct 2023 09:07:53 +0100 Subject: [PATCH 01/29] Use the object templates with least terrains allowed. --- lib/rmg/RmgObject.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 9934602a5..de3706839 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -130,6 +130,16 @@ void Object::Instance::setTemplate(TerrainId terrain, CRandomGenerator & rng) auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated(); throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName)); } + //Get terrain-specific template if possible + int leastTerrains = (*boost::min_element(templates, [](const std::shared_ptr & tmp1, const std::shared_ptr & tmp2) + { + return tmp1->getAllowedTerrains().size() < tmp2->getAllowedTerrains().size(); + }))->getAllowedTerrains().size(); + + vstd::erase_if(templates, [leastTerrains](const std::shared_ptr & tmp) + { + return tmp->getAllowedTerrains().size() > leastTerrains; + }); dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng); dAccessibleAreaCache.clear(); From 38847094f04f1f808653f0b6f66bb3438d3389ca Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Sat, 9 Dec 2023 08:15:36 +0100 Subject: [PATCH 02/29] Update Readme.md Added some vanilla screenshots --- docs/Readme.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/Readme.md b/docs/Readme.md index 6902f1bf6..a9b93d8a2 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -4,7 +4,16 @@ # VCMI Project -VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities. +VCMI is an open-source recreation of Heroes of Might & Magic III engine, giving it new and extended possibilities. + +

+Vanilla town siege in extended window +Vanilla town view with radial menu for touchscreen devices +Map editor +Large Spellbook with German translation +New widget for Hero selection, featuring Pavillon Town +

+ ## Links @@ -27,6 +36,14 @@ Please see corresponding installation guide articles for details for your platfo - [Android](players/Installation_Android.md) - [iOS](players/Installation_iOS.md) +

+Forge Town in battle +Asylum town with new creature dialog +Ruins town siege +Random map with new terrains +Launcher with translation support +

+ ## Documentation and guidelines for players - [General information about VCMI Project](players/Manual.md) From 842856f79a44081eb1919ce95e7231428168ffc9 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Sat, 9 Dec 2023 08:21:37 +0100 Subject: [PATCH 03/29] Update Readme.md --- docs/Readme.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/Readme.md b/docs/Readme.md index a9b93d8a2..38631e529 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -9,7 +9,6 @@ VCMI is an open-source recreation of Heroes of Might & Magic III engine, giving

Vanilla town siege in extended window Vanilla town view with radial menu for touchscreen devices -Map editor Large Spellbook with German translation New widget for Hero selection, featuring Pavillon Town

@@ -40,8 +39,7 @@ Please see corresponding installation guide articles for details for your platfo Forge Town in battle Asylum town with new creature dialog Ruins town siege -Random map with new terrains -Launcher with translation support + Map editor

## Documentation and guidelines for players From 0f23dde85d1853e0208a1c49d93e8e0a743e4fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 9 Dec 2023 14:25:03 +0100 Subject: [PATCH 04/29] Fixed crash due to incorrect monolith id scopes --- lib/mapObjectConstructors/CObjectClassesHandler.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 928a72a29..169cc2753 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -452,10 +452,14 @@ void CObjectClassesHandler::generateExtraMonolithsForRMG(ObjectClass * container newPortal->type = portal->getIndex(); newPortal->subtype = portalVec.size(); //indexes must be unique, they are returned as a set + newPortal->blockVisit = portal->blockVisit; + newPortal->removable = portal->removable; portalVec.push_back(newPortal); - registerObject(ModScope::scopeGame(), container->getJsonKey(), newPortal->subTypeName, newPortal->subtype); + + registerObject(newPortal->modScope, container->getJsonKey(), newPortal->subTypeName, newPortal->subtype); + //registerObject(ModScope::scopeBuiltin(), container->getJsonKey(), newPortal->subTypeName, newPortal->subtype); } } From cef25cca03ff2a423d44da8a7f7d2f90d82dbfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 9 Dec 2023 14:59:09 +0100 Subject: [PATCH 05/29] Fix for starting hero being passable --- lib/mapObjects/CGHeroInstance.cpp | 7 +++++++ lib/mapObjects/CGHeroInstance.h | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 4037cec39..de08279d0 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -150,6 +150,11 @@ bool CGHeroInstance::isCoastVisitable() const return true; } +bool CGHeroInstance::isBlockedVisitable() const +{ + return true; +} + BattleField CGHeroInstance::getBattlefield() const { return BattleField::NONE; @@ -281,6 +286,7 @@ CGHeroInstance::CGHeroInstance(): ID = Obj::HERO; secSkills.emplace_back(SecondarySkill::NONE, -1); blockVisit = true; + removable = true; } PlayerColor CGHeroInstance::getOwner() const @@ -301,6 +307,7 @@ void CGHeroInstance::setHeroType(HeroTypeID heroType) void CGHeroInstance::initHero(CRandomGenerator & rand, const HeroTypeID & SUBID) { + blockVisit = true; subID = SUBID.getNum(); initHero(rand); } diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 208ea27be..9f2484733 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -301,6 +301,7 @@ public: void updateFrom(const JsonNode & data) override; bool isCoastVisitable() const override; + bool isBlockedVisitable() const override; BattleField getBattlefield() const override; protected: void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;//synchr From 134f78113ef03dbbf50930da230fed5268f637dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 9 Dec 2023 15:20:11 +0100 Subject: [PATCH 06/29] Remove unneccessary code --- lib/mapObjects/CGHeroInstance.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index de08279d0..697d4f162 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -285,8 +285,6 @@ CGHeroInstance::CGHeroInstance(): setNodeType(HERO); ID = Obj::HERO; secSkills.emplace_back(SecondarySkill::NONE, -1); - blockVisit = true; - removable = true; } PlayerColor CGHeroInstance::getOwner() const @@ -307,7 +305,6 @@ void CGHeroInstance::setHeroType(HeroTypeID heroType) void CGHeroInstance::initHero(CRandomGenerator & rand, const HeroTypeID & SUBID) { - blockVisit = true; subID = SUBID.getNum(); initHero(rand); } From d15e9f886c6cf7b5e4970eabd04fe7638f4cc475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 9 Dec 2023 15:35:32 +0100 Subject: [PATCH 07/29] Cleanup --- lib/mapObjectConstructors/CObjectClassesHandler.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 169cc2753..d2a4907f1 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -457,9 +457,7 @@ void CObjectClassesHandler::generateExtraMonolithsForRMG(ObjectClass * container portalVec.push_back(newPortal); - registerObject(newPortal->modScope, container->getJsonKey(), newPortal->subTypeName, newPortal->subtype); - //registerObject(ModScope::scopeBuiltin(), container->getJsonKey(), newPortal->subTypeName, newPortal->subtype); } } From df78c9c6f19e84345f957e8f1573d4ae554b705a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 9 Dec 2023 18:08:22 +0200 Subject: [PATCH 08/29] Added workaround for crashes with outdated mods --- lib/mapObjectConstructors/CommonConstructors.cpp | 2 +- lib/spells/effects/Summon.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index 13f286cbf..6a558b96a 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -133,7 +133,7 @@ void CHeroInstanceConstructor::afterLoadFinalization() { filters[entry.first] = LogicalExpression(entry.second, [](const JsonNode & node) { - return HeroTypeID(VLC->identifiers()->getIdentifier("hero", node.Vector()[0]).value()); + return HeroTypeID(VLC->identifiers()->getIdentifier("hero", node.Vector()[0]).value_or(-1)); }); } } diff --git a/lib/spells/effects/Summon.cpp b/lib/spells/effects/Summon.cpp index ac3c0206b..39b4dceca 100644 --- a/lib/spells/effects/Summon.cpp +++ b/lib/spells/effects/Summon.cpp @@ -42,6 +42,12 @@ void Summon::adjustTargetTypes(std::vector & types) const bool Summon::applicable(Problem & problem, const Mechanics * m) const { + if (creature == CreatureID::NONE) + { + logMod->error("Attempt to summon non-existing creature!"); + return m->adaptGenericProblem(problem); + } + if(exclusive) { //check if there are summoned creatures of other type From 902db091da25f4f2963678c87eb9bdb5819167ab Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 9 Dec 2023 18:09:10 +0200 Subject: [PATCH 09/29] Simple fix for slider activation when clicking on left/right buttons --- client/widgets/Slider.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/widgets/Slider.cpp b/client/widgets/Slider.cpp index b8dfc98bf..8a37b6f46 100644 --- a/client/widgets/Slider.cpp +++ b/client/widgets/Slider.cpp @@ -156,6 +156,11 @@ void CSlider::clickPressed(const Point & cursorPosition) bool CSlider::receiveEvent(const Point &position, int eventType) const { + if (eventType == LCLICK) + { + return pos.isInside(position) && !left->pos.isInside(position) && !right->pos.isInside(position); + } + if(eventType != WHEEL && eventType != GESTURE) { return CIntObject::receiveEvent(position, eventType); From 2261298d09f54cd2f8030b5a85e6db914a5ab540 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 9 Dec 2023 18:09:38 +0200 Subject: [PATCH 10/29] Revert U-turns block. Actually possible in H3 and has unintended side effects --- lib/pathfinder/CPathfinder.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/pathfinder/CPathfinder.cpp b/lib/pathfinder/CPathfinder.cpp index 46d4f564b..28d510ee9 100644 --- a/lib/pathfinder/CPathfinder.cpp +++ b/lib/pathfinder/CPathfinder.cpp @@ -162,9 +162,6 @@ void CPathfinder::calculatePaths() if(neighbour->locked) continue; - if (source.node->theNodeBefore && source.node->theNodeBefore->coord == neighbour->coord ) - continue; // block U-turns - if(!hlp->isLayerAvailable(neighbour->layer)) continue; From 2de7a3939a4de12b59de0dc44e1e3314ef2ef7ba Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 9 Dec 2023 18:09:57 +0200 Subject: [PATCH 11/29] Fix text identifier for generic signs without custom text --- lib/mapObjects/MiscObjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 11c532f13..872e57ba4 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -937,7 +937,7 @@ void CGSignBottle::initObj(CRandomGenerator & rand) { auto vector = VLC->generaltexth->findStringsWithPrefix("core.randsign"); std::string messageIdentifier = *RandomGeneratorUtil::nextItem(vector, rand); - message.appendTextID(TextIdentifier("core", "randsign", messageIdentifier).get()); + message.appendTextID(messageIdentifier); } if(ID == Obj::OCEAN_BOTTLE) From b8e40978a077389bf2ef894964f9903c3cbbd1d4 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sat, 9 Dec 2023 17:29:57 +0100 Subject: [PATCH 12/29] stop smooth scroll when using window borders to scroll --- client/mapView/MapView.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/mapView/MapView.cpp b/client/mapView/MapView.cpp index b39c8a6f5..d1375f1df 100644 --- a/client/mapView/MapView.cpp +++ b/client/mapView/MapView.cpp @@ -122,7 +122,10 @@ void MapView::onMapLevelSwitched() void MapView::onMapScrolled(const Point & distance) { if(!isGesturing()) + { + postSwipeSpeed = 0.0; controller->setViewCenter(model->getMapViewCenter() + distance, model->getLevel()); + } } void MapView::onMapSwiped(const Point & viewPosition) From 9a52131c82f48fd1a6c454cdca333e749f14a2e0 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 9 Dec 2023 18:48:53 +0200 Subject: [PATCH 13/29] Use battle side instead of player color for fire shield damage formula --- server/battles/BattleActionProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index 56878c270..07180c913 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -1032,7 +1032,7 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const const CStack * actor = item.first; int64_t rawDamage = item.second; - const CGHeroInstance * actorOwner = battle.battleGetFightingHero(actor->unitOwner()); + const CGHeroInstance * actorOwner = battle.battleGetFightingHero(actor->unitSide()); if(actorOwner) { From adb720c8ea41892179c0630c1ad903a409f04160 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 9 Dec 2023 19:03:05 +0200 Subject: [PATCH 14/29] Bump version to 1.4.1 --- Mods/vcmi/mod.json | 2 +- android/vcmi-app/build.gradle | 4 ++-- cmake_modules/VersionDefinition.cmake | 2 +- debian/changelog | 6 ++++++ launcher/eu.vcmi.VCMI.metainfo.xml | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Mods/vcmi/mod.json b/Mods/vcmi/mod.json index cedf5bf5b..7d57c29d3 100644 --- a/Mods/vcmi/mod.json +++ b/Mods/vcmi/mod.json @@ -98,7 +98,7 @@ ] }, - "version" : "1.3", + "version" : "1.4", "author" : "VCMI Team", "contact" : "http://forum.vcmi.eu/index.php", "modType" : "Graphical", diff --git a/android/vcmi-app/build.gradle b/android/vcmi-app/build.gradle index 3b7d295ea..f46b2f8e8 100644 --- a/android/vcmi-app/build.gradle +++ b/android/vcmi-app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "is.xyz.vcmi" minSdk 19 targetSdk 33 - versionCode 1400 - versionName "1.4.0" + versionCode 1410 + versionName "1.4.1" setProperty("archivesBaseName", "vcmi") } diff --git a/cmake_modules/VersionDefinition.cmake b/cmake_modules/VersionDefinition.cmake index f9fdb6992..666d32737 100644 --- a/cmake_modules/VersionDefinition.cmake +++ b/cmake_modules/VersionDefinition.cmake @@ -1,6 +1,6 @@ set(VCMI_VERSION_MAJOR 1) set(VCMI_VERSION_MINOR 4) -set(VCMI_VERSION_PATCH 0) +set(VCMI_VERSION_PATCH 1) add_definitions( -DVCMI_VERSION_MAJOR=${VCMI_VERSION_MAJOR} -DVCMI_VERSION_MINOR=${VCMI_VERSION_MINOR} diff --git a/debian/changelog b/debian/changelog index 0c7935b15..253546373 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +vcmi (1.4.1) jammy; urgency=medium + + * New upstream release + + -- Ivan Savenko Fri, 22 Dec 2023 16:00:00 +0200 + vcmi (1.4.0) jammy; urgency=medium * New upstream release diff --git a/launcher/eu.vcmi.VCMI.metainfo.xml b/launcher/eu.vcmi.VCMI.metainfo.xml index 26ef16ef5..e7190b565 100644 --- a/launcher/eu.vcmi.VCMI.metainfo.xml +++ b/launcher/eu.vcmi.VCMI.metainfo.xml @@ -68,6 +68,7 @@ StrategyGame + From 91ebac3c2eb636f812e29885a2f948e75c694c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sun, 10 Dec 2023 09:46:20 +0100 Subject: [PATCH 15/29] Fix incorrect check --- lib/rmg/CMapGenOptions.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index 72d9e786c..0e147a621 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -730,12 +730,6 @@ std::vector CMapGenOptions::getPossibleTemplates() const return true; } - if(compOnlyPlayerCount != CMapGenOptions::RANDOM_SIZE) - { - if (!tmpl->getHumanPlayers().isInRange(compOnlyPlayerCount)) - return true; - } - return false; }); From d0e100c1bbf582c8dd5d1426946fed6c98bcb73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sun, 10 Dec 2023 09:50:25 +0100 Subject: [PATCH 16/29] Fix unused variable --- lib/rmg/CMapGenOptions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index 0e147a621..4f89f7398 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -580,7 +580,7 @@ void CMapGenOptions::finalize(CRandomGenerator & rand) } logGlobal->trace("Player %d: %s", player.second.getColor(), playerType); } - logGlobal->info("Final player config: %d total, %d cpu-only", players.size(), static_cast(getCompOnlyPlayerCount())); + logGlobal->info("Final player config: %d total, %d cpu-only", players.size(), cpuOnlyPlayers); } void CMapGenOptions::updatePlayers() From e74c2262c3e2b0b28632bb70dcea3a524dc64cec Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 10 Dec 2023 13:38:58 +0100 Subject: [PATCH 17/29] add option to gui to disable smooth dragging --- Mods/vcmi/config/vcmi/english.json | 2 ++ Mods/vcmi/config/vcmi/german.json | 2 ++ client/windows/settings/AdventureOptionsTab.cpp | 8 ++++++++ config/widgets/settings/adventureOptionsTab.json | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 242099ee7..557628769 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -129,6 +129,8 @@ "vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info Panel Creature Management}\n\nAllows rearranging creatures in info panel instead of cycling between default components.", "vcmi.adventureOptions.leftButtonDrag.hover" : "Left Click Drag Map", "vcmi.adventureOptions.leftButtonDrag.help" : "{Left Click Drag Map}\n\nWhen enabled, moving mouse with left button pressed will drag adventure map view.", + "vcmi.adventureOptions.smoothDragging.hover" : "Smooth Map Dragging", + "vcmi.adventureOptions.smoothDragging.help" : "{Smooth Map Dragging}\n\nWhen enabled, map dragging has a modern run out effect.", "vcmi.adventureOptions.mapScrollSpeed1.hover": "", "vcmi.adventureOptions.mapScrollSpeed5.hover": "", "vcmi.adventureOptions.mapScrollSpeed6.hover": "", diff --git a/Mods/vcmi/config/vcmi/german.json b/Mods/vcmi/config/vcmi/german.json index 07d13f06d..f2a2ed91c 100644 --- a/Mods/vcmi/config/vcmi/german.json +++ b/Mods/vcmi/config/vcmi/german.json @@ -129,6 +129,8 @@ "vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info-Panel Kreaturenmanagement}\n\nErmöglicht die Neuanordnung von Kreaturen im Info-Panel, anstatt zwischen den Standardkomponenten zu wechseln", "vcmi.adventureOptions.leftButtonDrag.hover" : "Ziehen der Karte mit Links", "vcmi.adventureOptions.leftButtonDrag.help" : "{Ziehen der Karte mit Links}\n\nWenn aktiviert, wird die Maus bei gedrückter linker Taste in die Kartenansicht gezogen", + "vcmi.adventureOptions.smoothDragging.hover" : "Nahtloses Ziehen der Karte", + "vcmi.adventureOptions.smoothDragging.help" : "{Nahtloses Ziehen der Karte}\n\nWenn aktiviert hat das Ziehen der Karte einen sanften Auslaufeffekt.", "vcmi.adventureOptions.mapScrollSpeed1.hover": "", "vcmi.adventureOptions.mapScrollSpeed5.hover": "", "vcmi.adventureOptions.mapScrollSpeed6.hover": "", diff --git a/client/windows/settings/AdventureOptionsTab.cpp b/client/windows/settings/AdventureOptionsTab.cpp index 90db19c93..67e7adbd4 100644 --- a/client/windows/settings/AdventureOptionsTab.cpp +++ b/client/windows/settings/AdventureOptionsTab.cpp @@ -126,6 +126,10 @@ AdventureOptionsTab::AdventureOptionsTab() { return setBoolSetting("adventure", "leftButtonDrag", value); }); + addCallback("smoothDraggingChanged", [](bool value) + { + return setBoolSetting("adventure", "smoothDragging", value); + }); build(config); std::shared_ptr playerHeroSpeedToggle = widget("heroMovementSpeedPicker"); @@ -164,4 +168,8 @@ AdventureOptionsTab::AdventureOptionsTab() std::shared_ptr leftButtonDragCheckbox = widget("leftButtonDragCheckbox"); if (leftButtonDragCheckbox) leftButtonDragCheckbox->setSelected(settings["adventure"]["leftButtonDrag"].Bool()); + + std::shared_ptr smoothDraggingCheckbox = widget("smoothDraggingCheckbox"); + if (smoothDraggingCheckbox) + smoothDraggingCheckbox->setSelected(settings["adventure"]["smoothDragging"].Bool()); } diff --git a/config/widgets/settings/adventureOptionsTab.json b/config/widgets/settings/adventureOptionsTab.json index cee89b6eb..c2f742062 100644 --- a/config/widgets/settings/adventureOptionsTab.json +++ b/config/widgets/settings/adventureOptionsTab.json @@ -391,6 +391,11 @@ "help": "vcmi.adventureOptions.leftButtonDrag", "callback": "leftButtonDragChanged", "created" : "desktop" + }, + { + "name": "smoothDraggingCheckbox", + "help": "vcmi.adventureOptions.smoothDragging", + "callback": "smoothDraggingChanged" } ] } From 76bd3dde7f2ac364f7a4d9cc8c7f18588d5ba301 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:29:11 +0100 Subject: [PATCH 18/29] missed hover --- config/widgets/settings/adventureOptionsTab.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/widgets/settings/adventureOptionsTab.json b/config/widgets/settings/adventureOptionsTab.json index c2f742062..96731dd4c 100644 --- a/config/widgets/settings/adventureOptionsTab.json +++ b/config/widgets/settings/adventureOptionsTab.json @@ -347,6 +347,9 @@ { "text": "vcmi.adventureOptions.leftButtonDrag.hover", "created" : "desktop" + }, + { + "text": "vcmi.adventureOptions.smoothDragging.hover" } ] }, From df2a5f05d865919d90fb39c889825ead5ec5c740 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:49:27 +0100 Subject: [PATCH 19/29] audio mute on focus lost setting --- Mods/vcmi/config/vcmi/english.json | 4 +++- Mods/vcmi/config/vcmi/german.json | 2 ++ client/eventsSDL/InputHandler.cpp | 4 ++-- client/windows/settings/GeneralOptionsTab.cpp | 9 ++++++++ config/schemas/settings.json | 7 +++++- .../widgets/settings/generalOptionsTab.json | 22 +++++++++++++++++++ 6 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 242099ee7..141973013 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -114,6 +114,8 @@ "vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Enhancements}\n\nToggle various quality of life interface improvements. Such as a backpack button etc. Disable to have a more classic experience.", "vcmi.systemOptions.enableLargeSpellbookButton.hover" : "Large Spell Book", "vcmi.systemOptions.enableLargeSpellbookButton.help" : "{Large Spell Book}\n\nEnables larger spell book that fits more spells per page. Spell book page change animation does not work with this setting enabled.", + "vcmi.systemOptions.audioMuteFocus.hover" : "Mute on inactivity", + "vcmi.systemOptions.audioMuteFocus.help" : "{Mute on inactivity}\n\nMute audio on inactive window focus. Exceptions are ingame messages and new turn sound.", "vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel", "vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in the info panel, instead of popping up in a separate window.", @@ -258,7 +260,7 @@ "vcmi.optionsTab.simturnsMin.help" : "Play simultaneously for specified number of days. Contacts between players during this period are blocked", "vcmi.optionsTab.simturnsMax.help" : "Play simultaneously for specified number of days or until contact with another player", "vcmi.optionsTab.simturnsAI.help" : "{Simultaneous AI Turns}\nExperimental option. Allows AI players to act at the same time as human player when simultaneous turns are enabled.", - + "vcmi.optionsTab.turnTime.select" : "Select turn timer preset", "vcmi.optionsTab.turnTime.unlimited" : "Unlimited turn time", "vcmi.optionsTab.turnTime.classic.1" : "Classic timer: 1 minute", diff --git a/Mods/vcmi/config/vcmi/german.json b/Mods/vcmi/config/vcmi/german.json index 07d13f06d..ed1c11a51 100644 --- a/Mods/vcmi/config/vcmi/german.json +++ b/Mods/vcmi/config/vcmi/german.json @@ -114,6 +114,8 @@ "vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Verbesserungen}\n\nSchaltet verschiedene Interface Verbesserungen um. Wie z.B. ein Rucksack-Button, etc. Deaktivieren, um ein klassischeres Erlebnis zu haben.", "vcmi.systemOptions.enableLargeSpellbookButton.hover" : "Großes Zauberbuch", "vcmi.systemOptions.enableLargeSpellbookButton.help" : "{Großes Zauberbuch}\n\nErmöglicht ein größeres Zauberbuch, in das mehr Zaubersprüche pro Seite passen. Die Animation des Seitenwechsels im Zauberbuch funktioniert nicht, wenn diese Einstellung aktiviert ist.", + "vcmi.systemOptions.audioMuteFocus.hover" : "Stumm bei Inaktivität", + "vcmi.systemOptions.audioMuteFocus.help" : "{Stumm bei Inaktivität}\n\nSchaltet Audio bei inaktiven Fenster-Fokus stumm. Ausnahmen sind Ingame-Nachrichten und der Neuer-Zug-Sound.", "vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen", "vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen", diff --git a/client/eventsSDL/InputHandler.cpp b/client/eventsSDL/InputHandler.cpp index 2db1020a9..adfbbde07 100644 --- a/client/eventsSDL/InputHandler.cpp +++ b/client/eventsSDL/InputHandler.cpp @@ -170,7 +170,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev) case SDL_WINDOWEVENT_FOCUS_GAINED: { boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); - if(settings["general"]["enableUiEnhancements"].Bool()) { + if(settings["general"]["audioMuteFocus"].Bool()) { CCS->musich->setVolume(settings["general"]["music"].Integer()); CCS->soundh->setVolume(settings["general"]["sound"].Integer()); } @@ -179,7 +179,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev) case SDL_WINDOWEVENT_FOCUS_LOST: { boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); - if(settings["general"]["enableUiEnhancements"].Bool()) { + if(settings["general"]["audioMuteFocus"].Bool()) { CCS->musich->setVolume(0); CCS->soundh->setVolume(0); } diff --git a/client/windows/settings/GeneralOptionsTab.cpp b/client/windows/settings/GeneralOptionsTab.cpp index c4a6ec84b..82d455c3d 100644 --- a/client/windows/settings/GeneralOptionsTab.cpp +++ b/client/windows/settings/GeneralOptionsTab.cpp @@ -167,6 +167,11 @@ GeneralOptionsTab::GeneralOptionsTab() setBoolSetting("gameTweaks", "enableLargeSpellbook", value); }); + addCallback("audioMuteFocusChanged", [](bool value) + { + setBoolSetting("general", "audioMuteFocus", value); + }); + //moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content addCallback("availableCreaturesAsDwellingChanged", [=](int value) { @@ -215,6 +220,10 @@ GeneralOptionsTab::GeneralOptionsTab() if (enableLargeSpellbookCheckbox) enableLargeSpellbookCheckbox->setSelected(settings["gameTweaks"]["enableLargeSpellbook"].Bool()); + std::shared_ptr audioMuteFocusCheckbox = widget("audioMuteFocusCheckbox"); + if (audioMuteFocusCheckbox) + audioMuteFocusCheckbox->setSelected(settings["general"]["audioMuteFocus"].Bool()); + std::shared_ptr musicSlider = widget("musicSlider"); musicSlider->scrollTo(CCS->musich->getVolume()); diff --git a/config/schemas/settings.json b/config/schemas/settings.json index c65cf374d..0ea20d327 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -39,7 +39,8 @@ "useSavePrefix", "savePrefix", "startTurnAutosave", - "enableUiEnhancements" + "enableUiEnhancements", + "audioMuteFocus" ], "properties" : { "playerName" : { @@ -131,6 +132,10 @@ "enableUiEnhancements" : { "type": "boolean", "default": true + }, + "audioMuteFocus" : { + "type": "boolean", + "default": false } } }, diff --git a/config/widgets/settings/generalOptionsTab.json b/config/widgets/settings/generalOptionsTab.json index 41d3b57a8..42348177e 100644 --- a/config/widgets/settings/generalOptionsTab.json +++ b/config/widgets/settings/generalOptionsTab.json @@ -180,6 +180,28 @@ "type": "labelCentered", "position": {"x": 565, "y": 158} }, + { + "type" : "verticalLayout", + "customType" : "labelDescription", + "position" : {"x": 415, "y": 202}, + "items" : [ + { + "text": "vcmi.systemOptions.audioMuteFocus.hover" + } + ] + }, + { + "type" : "verticalLayout", + "customType" : "checkbox", + "position" : {"x": 380, "y": 200}, + "items" : [ + { + "name": "audioMuteFocusCheckbox", + "help": "vcmi.systemOptions.audioMuteFocus", + "callback": "audioMuteFocusChanged" + } + ] + }, /////////////////////////////////////// Bottom section - Towns Settings { "type" : "verticalLayout", From ee7bd87b8d6512a7d63238f50ec4026e8dabbdab Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 16:14:01 +0200 Subject: [PATCH 20/29] Fix crash on losing mission-critical hero in battle --- lib/gameState/CGameState.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 519b23e8f..5d49b350e 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -1444,18 +1444,22 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio { // list of players that need to control object to fulfull condition // NOTE: cgameinfocallback specified explicitly in order to get const version - const auto & team = CGameInfoCallback::getPlayerTeam(player)->players; + const auto * team = CGameInfoCallback::getPlayerTeam(player); if (condition.objectID != ObjectInstanceID::NONE) // mode A - flag one specific object, like town { - return team.count(getObjInstance(condition.objectID)->tempOwner) != 0; + const auto * object = getObjInstance(condition.objectID); + + if (!object) + return false; + return team->players.count(object->getOwner()) != 0; } else { for(const auto & elem : map->objects) // mode B - flag all objects of this type { //check not flagged objs - if ( elem && elem->ID == condition.objectType.as() && team.count(elem->tempOwner) == 0 ) + if ( elem && elem->ID == condition.objectType.as() && team->players.count(elem->getOwner()) == 0 ) return false; } return true; From a7c838036df5d4131e73807bd211416e3c53e9cc Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 16:32:43 +0200 Subject: [PATCH 21/29] Workaround to avoid crash on invalid bonus --- server/battles/BattleActionProcessor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index 07180c913..f61d04548 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -1088,7 +1088,10 @@ void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bo TConstBonusListPtr spells = attacker->getBonuses(Selector::type()(attackMode)); for(const auto & sf : *spells) { - spellsToCast.insert(sf->subtype.as()); + if (sf->subtype.as() != SpellID()) + spellsToCast.insert(sf->subtype.as()); + else + logMod->error("Invalid spell to cast during attack!"); } for(SpellID spellID : spellsToCast) { From 999db2ed78c339000ec340cc21401792958cca76 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 16:33:22 +0200 Subject: [PATCH 22/29] Avoid boost::format that throws exception on invalid format string --- lib/MetaString.cpp | 5 ++++- lib/mapObjects/CGTownInstance.cpp | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/MetaString.cpp b/lib/MetaString.cpp index 8fdb6d3a0..2845a6f38 100644 --- a/lib/MetaString.cpp +++ b/lib/MetaString.cpp @@ -168,7 +168,10 @@ DLL_LINKAGE std::string MetaString::toString() const boost::replace_first(dst, "%d", std::to_string(numbers[nums++])); break; case EMessage::REPLACE_POSITIVE_NUMBER: - boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++])); + if (dst.find("%+d") != std::string::npos) + boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++])); + else + boost::replace_first(dst, "%d", std::to_string(numbers[nums++])); break; default: logGlobal->error("MetaString processing error! Received message of type %d", static_cast(elem)); diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index ab3fe7cd7..0f7c49bcf 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1224,12 +1224,21 @@ TerrainId CGTownInstance::getNativeTerrain() const GrowthInfo::Entry::Entry(const std::string &format, int _count) : count(_count) { - description = boost::str(boost::format(format) % count); + MetaString formatter; + formatter.appendRawString(format); + formatter.replacePositiveNumber(count); + + description = formatter.toString(); } GrowthInfo::Entry::Entry(int subID, const BuildingID & building, int _count): count(_count) { - description = boost::str(boost::format("%s %+d") % (*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated() % count); + MetaString formatter; + formatter.appendRawString("%s %+d"); + formatter.replaceRawString((*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated()); + formatter.replacePositiveNumber(count); + + description = formatter.toString(); } GrowthInfo::Entry::Entry(int _count, std::string fullDescription): From 705718abc1cae4461018e35bab29ae97e0a4819e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 19:16:45 +0200 Subject: [PATCH 23/29] Do not alter case of mod description --- launcher/modManager/cmodlistview_moc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 23789707d..9bfffda43 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -313,7 +313,7 @@ QString CModListView::genModInfoText(CModEntry & mod) result += replaceIfNotEmpty(getModNames(mod.getDependencies()), lineTemplate.arg(tr("Required mods"))); result += replaceIfNotEmpty(getModNames(mod.getConflicts()), lineTemplate.arg(tr("Conflicting mods"))); - result += replaceIfNotEmpty(getModNames(mod.getValue("description").toStringList()), textTemplate.arg(tr("Description"))); + result += replaceIfNotEmpty(mod.getValue("description"), textTemplate.arg(tr("Description"))); result += "

"; // to get some empty space From 3b6d3dee690b863a9c27a9f77d20510e26d2922e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 19:17:09 +0200 Subject: [PATCH 24/29] Slayer spell should only affect creatures with KING bonus --- lib/battle/DamageCalculator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/battle/DamageCalculator.cpp b/lib/battle/DamageCalculator.cpp index dfed47953..d735222f4 100644 --- a/lib/battle/DamageCalculator.cpp +++ b/lib/battle/DamageCalculator.cpp @@ -132,6 +132,9 @@ int DamageCalculator::getActorAttackSlayer() const const std::string cachingStrSlayer = "type_SLAYER"; static const auto selectorSlayer = Selector::type()(BonusType::SLAYER); + if (!info.defender->hasBonusOfType(BonusType::KING)) + return 0; + auto slayerEffects = info.attacker->getBonuses(selectorSlayer, cachingStrSlayer); auto slayerAffected = info.defender->unitType()->valOfBonuses(Selector::type()(BonusType::KING)); From c0572b061a71c88a08fa12dc8b336add5f72ca78 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 19:17:24 +0200 Subject: [PATCH 25/29] Fix compile on FreeBSD --- lib/modding/CModVersion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modding/CModVersion.h b/lib/modding/CModVersion.h index e0ae7c8be..221decb88 100644 --- a/lib/modding/CModVersion.h +++ b/lib/modding/CModVersion.h @@ -10,7 +10,7 @@ #pragma once -#ifdef __UCLIBC__ +#if defined(__UCLIBC__) || defined(__FreeBSD__) #undef major #undef minor #undef patch From 65721123a149205e774ade32bd67451a27e4213e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 19:17:58 +0200 Subject: [PATCH 26/29] Partial fix for Coronius specialty bug --- lib/spells/CSpellHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 1acb58c6b..aeceb6f7f 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -107,8 +107,8 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int32_t level) const { if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS) { - logGlobal->error("CSpell::getLevelInfo: invalid school level %d", level); - return levels.at(0); + logGlobal->error("CSpell::getLevelInfo: invalid school mastery level %d", level); + return levels.at(MasteryLevel::EXPERT); } return levels.at(level); From e23cddac8c9caa2a07537031a38ffa6a523c933d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 19:18:15 +0200 Subject: [PATCH 27/29] Fix AI movement --- server/CGameHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4d989108f..c46241587 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1133,7 +1133,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo if (guardian && getVisitingHero(guardian) != nullptr) return complainRet("Cannot move hero, destination monster is busy!"); - if (objectToVisit && getVisitingHero(objectToVisit) != nullptr) + if (objectToVisit && getVisitingHero(objectToVisit) != nullptr && getVisitingHero(objectToVisit) != h) return complainRet("Cannot move hero, destination object is busy!"); if (objectToVisit && From b62e801530eab1d613ab082a05aafe983174ebcf Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 19:48:27 +0200 Subject: [PATCH 28/29] Fix uninitialized variable in Seer Huts --- lib/rewardable/Limiter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rewardable/Limiter.cpp b/lib/rewardable/Limiter.cpp index e6fd3b361..f4a03edfd 100644 --- a/lib/rewardable/Limiter.cpp +++ b/lib/rewardable/Limiter.cpp @@ -30,6 +30,7 @@ Rewardable::Limiter::Limiter() , heroLevel(-1) , manaPercentage(0) , manaPoints(0) + , canLearnSkills(false) , primary(GameConstants::PRIMARY_SKILLS, 0) { } @@ -45,6 +46,7 @@ bool operator==(const Rewardable::Limiter & l, const Rewardable::Limiter & r) && l.manaPoints == r.manaPoints && l.manaPercentage == r.manaPercentage && l.secondary == r.secondary + && l.canLearnSkills == r.canLearnSkills && l.creatures == r.creatures && l.spells == r.spells && l.artifacts == r.artifacts From 7187ba2d7928ba8f065391b49997a9b36967ec47 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 10 Dec 2023 19:48:44 +0200 Subject: [PATCH 29/29] Fix crash on visiting Seer Hut with no reward --- lib/mapObjects/CRewardableObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index c05fbd9aa..ce8399115 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -59,7 +59,7 @@ std::vector CRewardableObject::loadComponents(const CGHeroInstance * if (rewardIndices.empty()) return result; - if (configuration.selectMode != Rewardable::SELECT_FIRST) + if (configuration.selectMode != Rewardable::SELECT_FIRST && rewardIndices.size() > 1) { for (auto index : rewardIndices) result.push_back(configuration.info.at(index).reward.getDisplayedComponent(contextHero));