From f2167904ed13112d5544c8749018c0c33fba17e7 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 12:02:57 +0300 Subject: [PATCH 01/25] update russian translation --- Mods/vcmi/config/vcmi/russian.json | 78 ++++++++++++++++++++++++++++-- Mods/vcmi/mod.json | 1 - 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/Mods/vcmi/config/vcmi/russian.json b/Mods/vcmi/config/vcmi/russian.json index 639880ae9..0d2c54484 100644 --- a/Mods/vcmi/config/vcmi/russian.json +++ b/Mods/vcmi/config/vcmi/russian.json @@ -25,13 +25,67 @@ "vcmi.server.errors.modsIncompatibility" : "Требуемые моды для загрузки игры:", "vcmi.server.confirmReconnect" : "Подключиться к предыдущей сессии?", + "vcmi.settingsMainWindow.generalTab.hover" : "Общее", + "vcmi.settingsMainWindow.generalTab.help" : "Переключиться на вкладку \"Общее\", содержащее общие настройки клиента игры", + "vcmi.settingsMainWindow.battleTab.hover" : "Бой", + "vcmi.settingsMainWindow.battleTab.help" : "Переключиться на вкладку \"Бой\", содержащее настройки поведения клиента игры в ходе битв", + "vcmi.settingsMainWindow.adventureTab.hover" : "Карта", + "vcmi.settingsMainWindow.adventureTab.help" : "Переключиться на вкладку \"Карта\", содержащее настройки поведения клиента на карте - там, где перемещаются герои", + + "vcmi.systemOptions.videoGroup" : "Видео", + "vcmi.systemOptions.audioGroup" : "Аудио", + "vcmi.systemOptions.otherGroup" : "Иное", // unused right now + "vcmi.systemOptions.townsGroup" : "Экран города", + "vcmi.systemOptions.fullscreenButton.hover" : "Полный экран", "vcmi.systemOptions.fullscreenButton.help" : "{Полный экран}\n\n Если выбрано, то VCMI будет работать в полноэкранном режиме, если нет - в окне", - "vcmi.systemOptions.resolutionButton.hover" : "Разрешение экрана", + "vcmi.systemOptions.resolutionButton.hover" : "Разрешение %wx%h", "vcmi.systemOptions.resolutionButton.help" : "{Разрешение экрана}\n\n Изменение разрешения экрана. Для применения нового разрешения требуется перезапуск игры.", "vcmi.systemOptions.resolutionMenu.hover" : "Выбрать разрешения экрана", "vcmi.systemOptions.resolutionMenu.help" : "Изменение разрешения экрана в игре.", "vcmi.systemOptions.fullscreenFailed" : "{Полный экран}\n\n Невозможно переключиться в полноэкранный режим - выбранное разрешение не поддерживается дисплеем!", + "vcmi.systemOptions.framerateButton.hover" : "Показывать частоту кадров", + "vcmi.systemOptions.framerateButton.help" : "{Показывать частоту кадров}\n\n Включить счетчик частоты кадров в углу игрового клиента", + + "vcmi.adventureOptions.numericQuantities.hover" : "Приблизительное число существ", + "vcmi.adventureOptions.numericQuantities.help" : "{Приблизительное число существ}\n\n Показывать приблизительное число существ в формате A-B вместо словесный обозначений", + "vcmi.adventureOptions.forceMovementInfo.hover" : "Всегда показывать стоимость перемещения", + "vcmi.adventureOptions.forceMovementInfo.help" : "{Всегда показывать стоимость перемещения}\n\n Заменить информацию в статусной строке на информацию о перемещении без необходимости нажатия {ALT}", + "vcmi.adventureOptions.showGrid.hover" : "Сетка", + "vcmi.adventureOptions.showGrid.help" : "{Сетка}\n\n Показывать сетку на видимой части карты.", + "vcmi.adventureOptions.mapSwipe.hover" : "Перемещение карты жестами", + "vcmi.adventureOptions.mapSwipe.help" : "{Перемещение карты жестами}\n\n Включает перемещение карты жестами на системах с сенсорным экраном. Сейчас также активируется левой кнопкой мыши.", + "vcmi.adventureOptions.mapScrollSpeed1.hover": "", + "vcmi.adventureOptions.mapScrollSpeed5.hover": "", + "vcmi.adventureOptions.mapScrollSpeed6.hover": "", + "vcmi.adventureOptions.mapScrollSpeed1.help": "Очень медленная прокрутка карты.", + "vcmi.adventureOptions.mapScrollSpeed5.help": "Очень быстрая прокрутка карты.", + "vcmi.adventureOptions.mapScrollSpeed6.help": "Мгновенная прокрутка карты.", + + "vcmi.battleOptions.queueSizeLabel.hover": "Показывать очередь хода существ", + "vcmi.battleOptions.queueSizeNoneButton.hover": "ВЫКЛ", + "vcmi.battleOptions.queueSizeAutoButton.hover": "АВТО", + "vcmi.battleOptions.queueSizeSmallButton.hover": "МАЛ", + "vcmi.battleOptions.queueSizeBigButton.hover": "БОЛ", + "vcmi.battleOptions.queueSizeNoneButton.help": "Полностью выключает видимость очереди хода существ в битве", + "vcmi.battleOptions.queueSizeAutoButton.help": "Размер очереди существ зависит от разрешения (малая при высоте экрана меньше 700 пикселей, иначе большая)", + "vcmi.battleOptions.queueSizeSmallButton.help": "Уменьшенный размер очереди существ", + "vcmi.battleOptions.queueSizeBigButton.help": "Увеличенный размер очереди существ (не поддерживается при разрешении менее 700 пикселей в высоту)", + "vcmi.battleOptions.animationsSpeed1.hover": "", + "vcmi.battleOptions.animationsSpeed5.hover": "", + "vcmi.battleOptions.animationsSpeed6.hover": "", + "vcmi.battleOptions.animationsSpeed1.help": "Очень медленная скорость анимации.", + "vcmi.battleOptions.animationsSpeed5.help": "Очень быстрая скорость анимации.", + "vcmi.battleOptions.animationsSpeed6.help": "Мгновенная скорость анимации.", + "vcmi.battleOptions.skipBattleIntroMusic.hover": "Пропускать вступительную музыку", + "vcmi.battleOptions.skipBattleIntroMusic.help": "{Пропускать вступительную музыку}\n\n Пропускать музыку, которая проигрывается в начале каждой битвы. Также может быть пропущена по нажатию {ESC}", + + "vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Показывать доступных существ", + "vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Показывать доступных существ}\n\n Показывать число доступных существ вместо прироста на экране города (в левом нижнем углу).", + "vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Показывать прирост существ", + "vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{Показывать прирост существ}\n\n Показывать прирост существ вместо числа доступных существ на экране города (в левом нижнем углу).", + "vcmi.otherOptions.compactTownCreatureInfo.hover": "Компактное окно информации о существах", + "vcmi.otherOptions.compactTownCreatureInfo.help": "{Компактное окно информации о существах}\n\n Уменьшить окно информации о существах в информации о городе", "vcmi.townHall.missingBase" : "Сначала необходимо построить: %s", "vcmi.townHall.noCreaturesToRecruit" : "Нет существ для найма!", @@ -66,10 +120,24 @@ "vcmi.questLog.hideComplete.hover" : "Скрыть завершенное", "vcmi.questLog.hideComplete.help" : "Скрыть все завершенные квесты", - "vcmi.randomMapTab.widgets.defaultTemplate" : "default", - "vcmi.randomMapTab.widgets.templateLabel" : "Template", - "vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Setup...", - "vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Team alignments", + "vcmi.randomMapTab.widgets.defaultTemplate" : "(по умолчанию)", + "vcmi.randomMapTab.widgets.templateLabel" : "Шаблон", + "vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Настройка...", + "vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Распределение команд", + "vcmi.randomMapTab.widgets.roadTypesLabel" : "Виды дорог", + + "mapObject.core.creatureBank.cyclopsStockpile.name" : "Хранилище циклопов", + "mapObject.core.creatureBank.dragonFlyHive.name" : "Улей летучих змиев", + "mapObject.core.creatureBank.dwarvenTreasury.name" : "Сокровищница гномов", + "mapObject.core.creatureBank.griffinConservatory.name" : "Консерватория грифонов", + "mapObject.core.creatureBank.inpCache.name" : "Яма бесов", + "mapObject.core.creatureBank.medusaStore.name" : "Склады медуз", + "mapObject.core.creatureBank.nagaBank.name" : "Хранилище наг", + "mapObject.core.crypt.crypt.name" : "Склеп", + "mapObject.core.derelictShip.derelictShip.name" : "Заброшенный корабль", + "mapObject.core.dragonUtopia.dragonUtopia.name" : "Утопия драконов", + "mapObject.core.pyramid.pyramid.name" : "Пирамида", + "mapObject.core.shipwreck.shipwreck.name" : "Кораблекрушение", // few strings from WoG used by vcmi "vcmi.stackExperience.description" : "» О п ы т с у щ е с т в «\n\nТип существа ................... : %s\nРанг опыта ................. : %s (%i)\nОчки опыта ............... : %i\nДо следующего .. : %i\nМаксимум за битву ... : %i%% (%i)\nЧисло в отряде .... : %i\nМаксимум новичков\n без потери ранга .... : %i\nМножитель опыта ........... : %.2f\nМножитель улучшения .......... : %.2f\nОпыт после 10 ранга ........ : %i\nМаксимум новичков для сохранения\n ранга 10 при максимальном опыте : %i", diff --git a/Mods/vcmi/mod.json b/Mods/vcmi/mod.json index 6df15eb6c..9059bbe57 100644 --- a/Mods/vcmi/mod.json +++ b/Mods/vcmi/mod.json @@ -32,7 +32,6 @@ "author" : "Команда VCMI", "modType" : "Графический", - "skipValidation" : true, "translations" : [ "config/vcmi/russian.json" ] From d503c875a47aec71830148fe46549c8f3ac86a2b Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 12:49:23 +0300 Subject: [PATCH 02/25] vcmi: fix mana points displayed --- lib/mapObjects/CRewardableObject.cpp | 31 +++++++++++++++------------- lib/mapObjects/CRewardableObject.h | 2 ++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 9dd973bcb..c5e4b20cd 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -297,19 +297,7 @@ void CRewardableObject::grantRewardBeforeLevelup(const CRewardVisitInfo & info, void CRewardableObject::grantRewardAfterLevelup(const CRewardVisitInfo & info, const CGHeroInstance * hero) const { if(info.reward.manaDiff || info.reward.manaPercentage >= 0) - { - si32 manaScaled = hero->mana; - if (info.reward.manaPercentage >= 0) - manaScaled = hero->manaLimit() * info.reward.manaPercentage / 100; - - si32 manaMissing = std::max(0, hero->manaLimit() - manaScaled); - si32 manaGranted = std::min(manaMissing, info.reward.manaDiff); - si32 manaOverflow = info.reward.manaDiff - manaGranted; - si32 manaOverLimit = manaOverflow * info.reward.manaOverflowFactor / 100; - si32 manaOutput = manaScaled + manaGranted + manaOverLimit; - - cb->setManaPoints(hero->id, manaOutput); - } + cb->setManaPoints(hero->id, info.reward.calculateManaPoints(hero)); if(info.reward.movePoints || info.reward.movePercentage >= 0) { @@ -448,7 +436,7 @@ void CRewardInfo::loadComponents(std::vector & comps, comps.emplace_back(Component::EXPERIENCE, 1, heroLevel, 0); if (manaDiff || manaPercentage >= 0) - comps.emplace_back(Component::PRIM_SKILL, 5, manaDiff, 0); + comps.emplace_back(Component::PRIM_SKILL, 5, calculateManaPoints(h) - h->mana, 0); for (size_t i=0; i & comps, } } +si32 CRewardInfo::calculateManaPoints(const CGHeroInstance * hero) const +{ + si32 manaScaled = hero->mana; + if (manaPercentage >= 0) + manaScaled = hero->manaLimit() * manaPercentage / 100; + + si32 manaMissing = std::max(0, hero->manaLimit() - manaScaled); + si32 manaGranted = std::min(manaMissing, manaDiff); + si32 manaOverflow = manaDiff - manaGranted; + si32 manaOverLimit = manaOverflow * manaOverflowFactor / 100; + si32 manaOutput = manaScaled + manaGranted + manaOverLimit; + + return manaOutput; +} + Component CRewardInfo::getDisplayedComponent(const CGHeroInstance * h) const { std::vector comps; diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 46be15e53..92b244f58 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -178,6 +178,8 @@ public: const CGHeroInstance * h) const; Component getDisplayedComponent(const CGHeroInstance * h) const; + si32 calculateManaPoints(const CGHeroInstance * h) const; + CRewardInfo() : heroExperience(0), heroLevel(0), From 0dbc7169d8132bc02ce3bafd3b7dd24a81c6c049 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 04:19:07 +0300 Subject: [PATCH 03/25] vcmi: fix creature numbers on infobar It was rendered on a border --- client/widgets/MiscWidgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 6a7c99ee8..72b28d6ac 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -270,7 +270,7 @@ void CArmyTooltip::init(const InfoAboutArmy &army) } } - subtitles.push_back(std::make_shared(slotsPos[slot.first.getNum()].x + 17, slotsPos[slot.first.getNum()].y + 41, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, subtitle)); + subtitles.push_back(std::make_shared(slotsPos[slot.first.getNum()].x + 17, slotsPos[slot.first.getNum()].y + 39, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, subtitle)); } } From 38d8585be37c31563aa081d65427284a0e7d4abb Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 03:34:04 +0300 Subject: [PATCH 04/25] vcmi: fix library of Enlinghtenment text Was text from +1 knowledge garden --- config/objects/rewardableOncePerHero.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/objects/rewardableOncePerHero.json b/config/objects/rewardableOncePerHero.json index 2f921669a..d052866bb 100644 --- a/config/objects/rewardableOncePerHero.json +++ b/config/objects/rewardableOncePerHero.json @@ -125,7 +125,7 @@ { "heroLevel" : 4, "secondary" : { "diplomacy" : 3 } } ] }, - "message" : 59, + "message" : 66, "primary" : { "attack" : 2, "defence" : 2, From 2fb2a79ca4966679daad162288d77cececed774b Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 6 Mar 2023 14:10:33 +0300 Subject: [PATCH 05/25] vcmi: fix components other than resources in infobar Do not redraw infobar when components shows and we request to show current adventure hero. Player already knows which hero he used to pick up components. --- client/CPlayerInterface.cpp | 25 ++++++++++++++++++++----- client/adventureMap/CInfoBar.cpp | 5 +++++ client/adventureMap/CInfoBar.h | 3 +++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index d5f60108b..bea2cee3d 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1773,6 +1773,16 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance * h) void CPlayerInterface::updateInfo(const CGObjectInstance * specific) { + bool isHero = dynamic_cast(specific) != nullptr; + bool changedHero = dynamic_cast(specific) != adventureInt->curHero(); + bool isTown = dynamic_cast(specific) != nullptr; + + bool update = (isHero && changedHero) || (isTown); + // If infobar is showing components and we request an update to hero + // do not force infobar tick here, it will prevents us to show components just picked up + if(adventureInt->infoBar->showingComponents() && !update) + return; + adventureInt->infoBar->showSelection(); } @@ -1883,14 +1893,16 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) void CPlayerInterface::artifactPut(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - adventureInt->infoBar->showSelection(); + auto hero = boost::apply_visitor(HeroObjectRetriever(), al.artHolder); + updateInfo(hero); askToAssembleArtifact(al); } void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - adventureInt->infoBar->showSelection(); + auto hero = boost::apply_visitor(HeroObjectRetriever(), al.artHolder); + updateInfo(hero); for(auto isa : GH.listInt) { auto artWin = dynamic_cast(isa.get()); @@ -1904,7 +1916,8 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) { EVENT_HANDLER_CALLED_BY_CLIENT; - adventureInt->infoBar->showSelection(); + auto hero = boost::apply_visitor(HeroObjectRetriever(), dst.artHolder); + updateInfo(hero); bool redraw = true; // If a bulk transfer has arrived, then redrawing only the last art movement. @@ -1932,7 +1945,8 @@ void CPlayerInterface::bulkArtMovementStart(size_t numOfArts) void CPlayerInterface::artifactAssembled(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - adventureInt->infoBar->showSelection(); + auto hero = boost::apply_visitor(HeroObjectRetriever(), al.artHolder); + updateInfo(hero); for(auto isa : GH.listInt) { auto artWin = dynamic_cast(isa.get()); @@ -1944,7 +1958,8 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al) void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - adventureInt->infoBar->showSelection(); + auto hero = boost::apply_visitor(HeroObjectRetriever(), al.artHolder); + updateInfo(hero); for(auto isa : GH.listInt) { auto artWin = dynamic_cast(isa.get()); diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index bf948e78e..722f700a5 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -275,6 +275,11 @@ void CInfoBar::showComponent(const Component & comp, std::string message) redraw(); } +bool CInfoBar::showingComponents() +{ + return state == COMPONENT; +} + void CInfoBar::startEnemyTurn(PlayerColor color) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 0c6106011..5b5809954 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -142,5 +142,8 @@ public: /// for 3 seconds shows amount of town halls and players status void showGameStatus(); + + /// check if infobar is showed something about pickups + bool showingComponents(); }; From 4617ce10e5e2cea0eb498ff7d62c673b070d3cb4 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 6 Mar 2023 02:30:21 +0300 Subject: [PATCH 06/25] vcmi: allow showing more than one components in infobar Up to 8, AFAIK. So, we can show multi-reward in infobox now. One issue remain - cannot dynamically choose components size based on text size. --- AI/Nullkiller/AIGateway.cpp | 2 +- AI/Nullkiller/AIGateway.h | 2 +- AI/VCAI/VCAI.cpp | 3 ++- AI/VCAI/VCAI.h | 2 +- client/CPlayerInterface.cpp | 18 +++++++--------- client/CPlayerInterface.h | 3 +-- client/Client.h | 1 - client/ClientNetPackVisitors.h | 1 - client/NetPacksClient.cpp | 7 +----- client/adventureMap/CInfoBar.cpp | 36 +++++++++++++++++++++++++------ client/adventureMap/CInfoBar.h | 9 ++++---- client/widgets/CComponent.cpp | 25 +++++++++++++++------ client/widgets/CComponent.h | 12 ++++++++++- lib/IGameCallback.h | 2 -- lib/IGameEventsReceiver.h | 4 ++-- lib/NetPackVisitor.h | 1 - lib/NetPacks.h | 18 ++-------------- lib/NetPacksBase.h | 7 ++++++ lib/NetPacksLib.cpp | 5 ----- lib/mapObjects/MiscObjects.cpp | 27 ++++++++++++----------- lib/registerTypes/RegisterTypes.h | 1 - server/CGameHandler.cpp | 4 ---- server/CGameHandler.h | 1 - test/mock/mock_IGameCallback.h | 1 - 24 files changed, 105 insertions(+), 87 deletions(-) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 5485d5b88..5f94dd1df 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -390,7 +390,7 @@ void AIGateway::advmapSpellCast(const CGHeroInstance * caster, int spellID) NET_EVENT_HANDLER; } -void AIGateway::showInfoDialog(const std::string & text, const std::vector & components, int soundID) +void AIGateway::showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector & components, int soundID) { LOG_TRACE_PARAMS(logAi, "soundID '%i'", soundID); NET_EVENT_HANDLER; diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index 98b0fca8b..1443392f3 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -153,7 +153,7 @@ public: void playerBonusChanged(const Bonus & bonus, bool gain) override; void heroCreated(const CGHeroInstance *) override; void advmapSpellCast(const CGHeroInstance * caster, int spellID) override; - void showInfoDialog(const std::string & text, const std::vector & components, int soundID) override; + void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector & components, int soundID) override; void requestRealized(PackageApplied * pa) override; void receivedResource() override; void objectRemoved(const CGObjectInstance * obj) override; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index da7eced29..83b272c9e 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -20,6 +20,7 @@ #include "../../lib/CHeroHandler.h" #include "../../lib/CModHandler.h" #include "../../lib/CGameState.h" +#include "../../lib/NetPacksBase.h" #include "../../lib/NetPacks.h" #include "../../lib/serializer/CTypeList.h" #include "../../lib/serializer/BinarySerializer.h" @@ -475,7 +476,7 @@ void VCAI::advmapSpellCast(const CGHeroInstance * caster, int spellID) NET_EVENT_HANDLER; } -void VCAI::showInfoDialog(const std::string & text, const std::vector & components, int soundID) +void VCAI::showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector & components, int soundID) { LOG_TRACE_PARAMS(logAi, "soundID '%i'", soundID); NET_EVENT_HANDLER; diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 5ea900961..249a91189 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -186,7 +186,7 @@ public: void playerBonusChanged(const Bonus & bonus, bool gain) override; void heroCreated(const CGHeroInstance *) override; void advmapSpellCast(const CGHeroInstance * caster, int spellID) override; - void showInfoDialog(const std::string & text, const std::vector & components, int soundID) override; + void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector & components, int soundID) override; void requestRealized(PackageApplied * pa) override; void receivedResource() override; void objectRemoved(const CGObjectInstance * obj) override; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index bea2cee3d..8d004ccd3 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1031,18 +1031,16 @@ void CPlayerInterface::yourTacticPhase(int distance) boost::this_thread::sleep(boost::posix_time::millisec(1)); } -void CPlayerInterface::showComp(const Component &comp, std::string message) +void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &text, const std::vector & components, int soundID) { EVENT_HANDLER_CALLED_BY_CLIENT; - waitWhileDialog(); //Fix for mantis #98 + if(type == InfoWindow::INFO) { + adventureInt->infoBar->showComponents(components, text); + if (makingTurn && GH.listInt.size() && LOCPLINT == this) + CCS->soundh->playSound(static_cast(soundID)); + return; + } - CCS->soundh->playSoundFromSet(CCS->soundh->pickupSounds); - adventureInt->infoBar->showComponent(comp, message); -} - -void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector & components, int soundID) -{ - EVENT_HANDLER_CALLED_BY_CLIENT; if (settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown()) { return; @@ -1093,7 +1091,7 @@ void CPlayerInterface::showInfoDialogAndWait(std::vector & components std::string str; text.toString(str); - showInfoDialog(str, components, 0); + showInfoDialog(EInfoWindowMode::MODAL, str, components, 0); waitWhileDialog(); } diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 0f1264c56..591ecf260 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -168,7 +168,7 @@ public: void heroMovePointsChanged(const CGHeroInstance * hero) override; void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town) override; void receivedResource() override; - void showInfoDialog(const std::string & text, const std::vector & components, int soundID) override; + void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector & components, int soundID) override; void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override; void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard; void showBlockingDialog(const std::string &text, const std::vector &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID. @@ -201,7 +201,6 @@ public: void playerBlocked(int reason, bool start) override; void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override; void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface - void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox void saveGame(BinarySerializer & h, const int version) override; //saving void loadGame(BinaryDeserializer & h, const int version) override; //loading void showWorldViewEx(const std::vector & objectPositions, bool showTerrain) override; diff --git a/client/Client.h b/client/Client.h index c51fbf759..140b5ad0f 100644 --- a/client/Client.h +++ b/client/Client.h @@ -217,7 +217,6 @@ public: void removeArtifact(const ArtifactLocation & al) override {}; bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override {return false;}; - void showCompInfo(ShowInInfobox * comp) override {}; void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {}; void visitCastleObjects(const CGTownInstance * obj, const CGHeroInstance * hero) override {}; void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {}; diff --git a/client/ClientNetPackVisitors.h b/client/ClientNetPackVisitors.h index 72d0a9f2e..20c51428c 100644 --- a/client/ClientNetPackVisitors.h +++ b/client/ClientNetPackVisitors.h @@ -96,7 +96,6 @@ public: void visitYourTurn(YourTurn & pack) override; void visitSaveGameClient(SaveGameClient & pack) override; void visitPlayerMessageClient(PlayerMessageClient & pack) override; - void visitShowInInfobox(ShowInInfobox & pack) override; void visitAdvmapSpellCast(AdvmapSpellCast & pack) override; void visitShowWorldViewEx(ShowWorldViewEx & pack) override; void visitOpenWindow(OpenWindow & pack) override; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index d41ca125f..dee2ebf71 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -592,7 +592,7 @@ void ApplyClientNetPackVisitor::visitInfoWindow(InfoWindow & pack) std::string str; pack.text.toString(str); - if(!callInterfaceIfPresent(cl, pack.player, &CGameInterface::showInfoDialog, str, pack.components,(soundBase::soundID)pack.soundID)) + if(!callInterfaceIfPresent(cl, pack.player, &CGameInterface::showInfoDialog, pack.type, str, pack.components,(soundBase::soundID)pack.soundID)) logNetwork->warn("We received InfoWindow for not our player..."); } @@ -855,11 +855,6 @@ void ApplyClientNetPackVisitor::visitPlayerMessageClient(PlayerMessageClient & p LOCPLINT->cingconsole->print(str.str()); } -void ApplyClientNetPackVisitor::visitShowInInfobox(ShowInInfobox & pack) -{ - callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::showComp, pack.c, pack.text.toString()); -} - void ApplyClientNetPackVisitor::visitAdvmapSpellCast(AdvmapSpellCast & pack) { cl.invalidatePaths(); diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index 722f700a5..d020635d6 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -163,16 +163,39 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo() } } -CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const Component & compToDisplay, std::string message) +CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector & compsToDisplay, std::string message) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATOT", 1, 0); + auto fullRect = Rect(4, 4, 171, 171); + auto textRect = fullRect; + auto imageRect = fullRect; - comp = std::make_shared(compToDisplay); - comp->moveTo(Point(pos.x+47, pos.y+50)); + if(!compsToDisplay.empty()) + { + auto size = CComponent::large; + if(compsToDisplay.size() > 2) + size = CComponent::small; + if(message != "") + { + textRect = Rect(4, 4, 171, 42); + imageRect = Rect(4, 42, 171, 121); + if(compsToDisplay.size() > 4) + size = CComponent::tiny; + } + else if(compsToDisplay.size() > 4) + size = CComponent::small; - text = std::make_shared(message, Rect(10, 4, 160, 50), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); + std::vector> vect; + + for(const auto & c : compsToDisplay) + vect.emplace_back(std::make_shared(c, size)); + + comps = std::make_shared(vect, imageRect, 4, 4, 1); + } + + text = std::make_shared(message, textRect, 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); } void CInfoBar::playNewDaySound() @@ -266,11 +289,12 @@ void CInfoBar::showDate() redraw(); } -void CInfoBar::showComponent(const Component & comp, std::string message) +void CInfoBar::showComponents(const std::vector & comps, std::string message) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); state = COMPONENT; - visibleInfo = std::make_shared(comp, message); + visibleInfo = std::make_shared(comps, message); + setTimer(3000); redraw(); } diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 5b5809954..be1a4234a 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -23,6 +23,7 @@ VCMI_LIB_NAMESPACE_END class CAnimImage; class CShowableAnim; class CComponent; +class CComponentBox; class CHeroTooltip; class CTownTooltip; class CLabel; @@ -97,10 +98,10 @@ class CInfoBar : public CIntObject class VisibleComponentInfo : public CVisibleInfo { - std::shared_ptr comp; + std::shared_ptr comps; std::shared_ptr text; public: - VisibleComponentInfo(const Component & compToDisplay, std::string message); + VisibleComponentInfo(const std::vector & compsToDisplay, std::string message); }; enum EState @@ -127,8 +128,8 @@ public: /// show new day/week animation void showDate(); - /// show component for 3 seconds. Used to display picked up resources - void showComponent(const Component & comp, std::string message); + /// show components for 3 seconds. Used to display picked up resources. Can display up to 8 components + void showComponents(const std::vector & comps, std::string message); /// print enemy turn progress void startEnemyTurn(PlayerColor color); diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index c5a5bd8f3..203bf4a96 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -336,9 +336,6 @@ Point CComponentBox::getOrTextPos(CComponent *left, CComponent *right) int CComponentBox::getDistance(CComponent *left, CComponent *right) { - static const int betweenImagesMin = 20; - static const int betweenSubtitlesMin = 10; - int leftSubtitle = ( left->pos.w - left->image->pos.w) / 2; int rightSubtitle = (right->pos.w - right->image->pos.w) / 2; int subtitlesOffset = leftSubtitle + rightSubtitle; @@ -348,8 +345,6 @@ int CComponentBox::getDistance(CComponent *left, CComponent *right) void CComponentBox::placeComponents(bool selectable) { - static const int betweenRows = 22; - OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); if (components.empty()) return; @@ -450,7 +445,15 @@ void CComponentBox::placeComponents(bool selectable) } CComponentBox::CComponentBox(std::vector> _components, Rect position): - components(_components) + CComponentBox(_components, position, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows) +{ +} + +CComponentBox::CComponentBox(std::vector> _components, Rect position, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows): + components(_components), + betweenImagesMin(betweenImagesMin), + betweenSubtitlesMin(betweenSubtitlesMin), + betweenRows(betweenRows) { type |= REDRAW_PARENT; pos = position + pos.topLeft(); @@ -458,8 +461,16 @@ CComponentBox::CComponentBox(std::vector> _component } CComponentBox::CComponentBox(std::vector> _components, Rect position, std::function _onSelect): + CComponentBox(_components, position, _onSelect, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows) +{ +} + +CComponentBox::CComponentBox(std::vector> _components, Rect position, std::function _onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows): components(_components.begin(), _components.end()), - onSelect(_onSelect) + onSelect(_onSelect), + betweenImagesMin(betweenImagesMin), + betweenSubtitlesMin(betweenSubtitlesMin), + betweenRows(betweenRows) { type |= REDRAW_PARENT; pos = position + pos.topLeft(); diff --git a/client/widgets/CComponent.h b/client/widgets/CComponent.h index ba7ad3065..b56453f99 100644 --- a/client/widgets/CComponent.h +++ b/client/widgets/CComponent.h @@ -94,6 +94,14 @@ class CComponentBox : public CIntObject std::shared_ptr selected; std::function onSelect; + static constexpr int defaultBetweenImagesMin = 20; + static constexpr int defaultBetweenSubtitlesMin = 10; + static constexpr int defaultBetweenRows = 22; + + int betweenImagesMin; + int betweenSubtitlesMin; + int betweenRows; + void selectionChanged(std::shared_ptr newSelection); //get position of "or" text between these comps @@ -108,11 +116,13 @@ public: /// return index of selected item int selectedIndex(); - /// constructor for non-selectable components + /// constructors for non-selectable components CComponentBox(std::vector> components, Rect position); + CComponentBox(std::vector> components, Rect position, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows); /// constructor for selectable components /// will also create "or" labels between components /// onSelect - optional function that will be called every time on selection change CComponentBox(std::vector> components, Rect position, std::function onSelect = nullptr); + CComponentBox(std::vector> components, Rect position, std::function onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows); }; diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 2d7a34105..6208ad349 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -21,7 +21,6 @@ struct GiveBonus; struct BlockingDialog; struct TeleportDialog; struct MetaString; -struct ShowInInfobox; struct StackLocation; struct ArtifactLocation; class CCreatureSet; @@ -113,7 +112,6 @@ public: virtual void removeArtifact(const ArtifactLocation &al) = 0; virtual bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) = 0; - virtual void showCompInfo(ShowInInfobox * comp)=0; virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0; virtual void visitCastleObjects(const CGTownInstance * obj, const CGHeroInstance * hero)=0; virtual void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0; diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index cc9964efc..a1da28237 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -10,6 +10,7 @@ #pragma once +#include "NetPacksBase.h" #include "battle/BattleHex.h" #include "GameConstants.h" #include "int3.h" @@ -103,7 +104,7 @@ public: virtual void heroMovePointsChanged(const CGHeroInstance * hero){} //not called at the beginning of turn and after movement virtual void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town){}; virtual void receivedResource(){}; - virtual void showInfoDialog(const std::string & text, const std::vector & components, int soundID){}; + virtual void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector & components, int soundID){}; virtual void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level){} virtual void showShipyardDialog(const IShipyard *obj){} //obj may be town or shipyard; state: 0 - can buid, 1 - lack of resources, 2 - dest tile is blocked, 3 - no water @@ -132,7 +133,6 @@ public: virtual void playerBlocked(int reason, bool start){}; //reason: 0 - upcoming battle virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) {}; //player lost or won the game virtual void playerStartsTurn(PlayerColor player){}; - virtual void showComp(const Component &comp, std::string message) {}; //display component in the advmapint infobox //TODO shouldn't be moved down the tree? virtual void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID){}; diff --git a/lib/NetPackVisitor.h b/lib/NetPackVisitor.h index 642b90c5a..7cef3d188 100644 --- a/lib/NetPackVisitor.h +++ b/lib/NetPackVisitor.h @@ -106,7 +106,6 @@ public: virtual void visitBattleSetStackProperty(BattleSetStackProperty & pack) {} virtual void visitBattleTriggerEffect(BattleTriggerEffect & pack) {} virtual void visitBattleUpdateGateState(BattleUpdateGateState & pack) {} - virtual void visitShowInInfobox(ShowInInfobox & pack) {} virtual void visitAdvmapSpellCast(AdvmapSpellCast & pack) {} virtual void visitShowWorldViewEx(ShowWorldViewEx & pack) {} virtual void visitEndTurn(EndTurn & pack) {} diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 4d823544f..c1d32d263 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1150,6 +1150,7 @@ struct DLL_LINKAGE NewTurn : public CPackForClient struct DLL_LINKAGE InfoWindow : public CPackForClient //103 - displays simple info window { + EInfoWindowMode type = EInfoWindowMode::MODAL; MetaString text; std::vector components; PlayerColor player; @@ -1159,6 +1160,7 @@ struct DLL_LINKAGE InfoWindow : public CPackForClient //103 - displays simple i template void serialize(Handler & h, const int version) { + h & type; h & text; h & components; h & player; @@ -1873,22 +1875,6 @@ protected: virtual void visitTyped(ICPackVisitor & visitor) override; }; -struct DLL_LINKAGE ShowInInfobox : public CPackForClient -{ - PlayerColor player; - Component c; - MetaString text; - template void serialize(Handler & h, const int version) - { - h & player; - h & c; - h & text; - } - -protected: - virtual void visitTyped(ICPackVisitor & visitor) override; -}; - struct DLL_LINKAGE AdvmapSpellCast : public CPackForClient { ObjectInstanceID casterID; diff --git a/lib/NetPacksBase.h b/lib/NetPacksBase.h index 16b322c3e..29c0d09fb 100644 --- a/lib/NetPacksBase.h +++ b/lib/NetPacksBase.h @@ -35,6 +35,13 @@ struct ArtSlotInfo; class ICPackVisitor; +enum class EInfoWindowMode : uint8_t +{ + AUTO, + MODAL, + INFO +}; + struct DLL_LINKAGE CPack { std::shared_ptr c; // Pointer to connection that pack received from diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 11c80d8ee..279af9bc8 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -492,11 +492,6 @@ void BattleUpdateGateState::visitTyped(ICPackVisitor & visitor) visitor.visitBattleUpdateGateState(*this); } -void ShowInInfobox::visitTyped(ICPackVisitor & visitor) -{ - visitor.visitShowInInfobox(*this); -} - void AdvmapSpellCast::visitTyped(ICPackVisitor & visitor) { visitor.visitAdvmapSpellCast(*this); diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index cdb1dddac..5f48f8ebf 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -881,27 +881,30 @@ void CGResource::onHeroVisit( const CGHeroInstance * h ) const } } else - { - if(message.length()) - { - InfoWindow iw; - iw.player = h->tempOwner; - iw.text << message; - cb->showInfoDialog(&iw); - } collectRes(h->getOwner()); - } } void CGResource::collectRes(const PlayerColor & player) const { cb->giveResource(player, static_cast(subID), amount); - ShowInInfobox sii; + InfoWindow sii; sii.player = player; - sii.c = Component(Component::RESOURCE,subID,amount,0); + if(!message.empty()) + { + sii.type = EInfoWindowMode::AUTO; + sii.text << message; + } + else + { + sii.type = EInfoWindowMode::INFO; + sii.text.addTxt(MetaString::ADVOB_TXT,113); + sii.text.addReplacement(MetaString::RES_NAMES, subID); + } + sii.components.emplace_back(Component::RESOURCE,subID,amount,0); sii.text.addTxt(MetaString::ADVOB_TXT,113); sii.text.addReplacement(MetaString::RES_NAMES, subID); - cb->showCompInfo(&sii); + sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6); + cb->showInfoDialog(&sii); cb->removeObject(this); } diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index cb543bd4c..e922c8aae 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -243,7 +243,6 @@ void registerTypesClientPacks1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4b979b028..4534b1618 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2657,10 +2657,6 @@ void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector Date: Mon, 6 Mar 2023 14:31:06 +0300 Subject: [PATCH 07/25] vcmi: allow showing simple rewardables in infobox --- lib/mapObjects/CRewardableConstructor.cpp | 2 ++ lib/mapObjects/CRewardableObject.cpp | 6 ++++-- lib/mapObjects/CRewardableObject.h | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/mapObjects/CRewardableConstructor.cpp b/lib/mapObjects/CRewardableConstructor.cpp index 63434041a..b5129a205 100644 --- a/lib/mapObjects/CRewardableConstructor.cpp +++ b/lib/mapObjects/CRewardableConstructor.cpp @@ -231,6 +231,8 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando object->canRefuse = parameters["canRefuse"].Bool(); + object->showInInfobox = parameters["showInInfobox"].Bool(); + auto visitMode = parameters["visitMode"].String(); for(int i = 0; i < Rewardable::VisitModeString.size(); ++i) { diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index c5e4b20cd..bb92bad65 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -134,13 +134,15 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const { auto vi = info[index]; logGlobal->debug("Granting reward %d. Message says: %s", index, vi.message.toString()); - // show message only if it is not empty - if (!vi.message.toString().empty()) + if (showInInfobox || !vi.message.toString().empty()) // show message only if it is not empty or in infobox { InfoWindow iw; iw.player = h->tempOwner; iw.text = vi.message; vi.reward.loadComponents(iw.components, h); + iw.type = EInfoWindowMode::AUTO; + if(showInInfobox) + iw.type = EInfoWindowMode::INFO; cb->showInfoDialog(&iw); } // grant reward afterwards. Note that it may remove object diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 92b244f58..03004da74 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -306,6 +306,9 @@ protected: /// if true - player can refuse visiting an object (e.g. Tomb) bool canRefuse; + /// if true - object info will shown in infobox (like resource pickup) + bool showInInfobox; + /// return true if this object was "cleared" before and no longer has rewards applicable to selected hero /// unlike wasVisited, this method uses information not available to player owner, for example, if object was cleared by another player before bool wasVisitedBefore(const CGHeroInstance * contextHero) const; @@ -345,6 +348,7 @@ public: h & static_cast(*this); h & info; h & canRefuse; + h & showInInfobox; h & resetParameters; h & onSelect; h & visitMode; From 7a04c28815306ca0f8fddf2be6c93a2b17e5399f Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 6 Mar 2023 21:22:54 +0300 Subject: [PATCH 08/25] vcmi: remove pickupSounds set It is unused now, resource pickups handled similar like necromancy --- client/CMusicHandler.cpp | 7 ------- client/CMusicHandler.h | 1 - 2 files changed, 8 deletions(-) diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index 995b55998..4acc87d98 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -77,13 +77,6 @@ CSoundHandler::CSoundHandler(): { listener(std::bind(&CSoundHandler::onVolumeChange, this, _1)); - // Vectors for helper(s) - pickupSounds = - { - soundBase::pickup01, soundBase::pickup02, soundBase::pickup03, - soundBase::pickup04, soundBase::pickup05, soundBase::pickup06, soundBase::pickup07 - }; - battleIntroSounds = { soundBase::battle00, soundBase::battle01, diff --git a/client/CMusicHandler.h b/client/CMusicHandler.h index 5c615a451..5d3319052 100644 --- a/client/CMusicHandler.h +++ b/client/CMusicHandler.h @@ -78,7 +78,6 @@ public: void ambientStopAllChannels(); // Sets - std::vector pickupSounds; std::vector battleIntroSounds; }; From da8840c499826feff860740e5629e765ec1c7535 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 03:18:40 +0300 Subject: [PATCH 09/25] CComponent: do not always assume 80 for text --- client/widgets/CComponent.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 203bf4a96..5505e4158 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -73,7 +73,13 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize) pos.h += 4; //distance between text and image - std::vector textLines = CMessage::breakText(getSubtitle(), std::max(80, pos.w), font); + auto max = 80; + if (size < medium) + max = 40; + if (size < small) + max = 30; + + std::vector textLines = CMessage::breakText(getSubtitle(), std::max(max, pos.w), font); for(auto & line : textLines) { int height = static_cast(graphics->fonts[font]->getLineHeight()); From 75358a68a719ada5e5555ef8e4351caf179e8047 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 03:28:25 +0300 Subject: [PATCH 10/25] vcmi: name infobar height and width --- client/adventureMap/CAdvMapInt.cpp | 2 +- client/adventureMap/CInfoBar.cpp | 4 ++++ client/adventureMap/CInfoBar.h | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/client/adventureMap/CAdvMapInt.cpp b/client/adventureMap/CAdvMapInt.cpp index 7793c0a12..2f3b820a2 100644 --- a/client/adventureMap/CAdvMapInt.cpp +++ b/client/adventureMap/CAdvMapInt.cpp @@ -86,7 +86,7 @@ CAdvMapInt::CAdvMapInt(): statusbar(CGStatusBar::create(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG)), heroList(new CHeroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD)), townList(new CTownList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD)), - infoBar(new CInfoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192))), + infoBar(new CInfoBar(Point(ADVOPT.infoboxX, ADVOPT.infoboxY))), resdatabar(new CResDataBar), mapAudio(new MapAudioPlayer()), terrain(new MapView(Point(ADVOPT.advmapX, ADVOPT.advmapY), Point(ADVOPT.advmapW, ADVOPT.advmapH))), diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index d020635d6..400eb7856 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -279,6 +279,10 @@ CInfoBar::CInfoBar(const Rect & position) reset(); } +CInfoBar::CInfoBar(const Point & position): CInfoBar(Rect(position.x, position.y, width, height)) +{ +} + void CInfoBar::showDate() { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index be1a4234a..6c6ea662d 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -32,6 +32,13 @@ class CTextBox; /// Info box which shows next week/day information, hold the current date class CInfoBar : public CIntObject { +private: + /// Infobar actually have a fixed size + /// Declare before to compute correct size of widgets + static constexpr int width = 192; + static constexpr int height = 192; + static constexpr int component_offset = 8; + //all visible information located in one object - for ease of replacing class CVisibleInfo : public CIntObject { @@ -124,6 +131,7 @@ class CInfoBar : public CIntObject void playNewDaySound(); public: CInfoBar(const Rect & pos); + CInfoBar(const Point & pos); /// show new day/week animation void showDate(); From a0c644a0e5215185f1a021ae72f6ad6eb4022608 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 03:29:04 +0300 Subject: [PATCH 11/25] vcmi: actually implement AUTO for CInfoWindow This is possible to use infobar interactions now. --- Mods/vcmi/config/vcmi/english.json | 2 + Mods/vcmi/config/vcmi/russian.json | 2 + client/CPlayerInterface.cpp | 17 +++-- client/adventureMap/CInfoBar.cpp | 67 ++++++++++++++++--- client/adventureMap/CInfoBar.h | 18 ++++- client/windows/CMessage.cpp | 19 ++++++ client/windows/CMessage.h | 6 ++ .../windows/settings/AdventureOptionsTab.cpp | 10 ++- config/schemas/settings.json | 5 ++ .../widgets/settings/adventureOptionsTab.json | 13 +++- 10 files changed, 138 insertions(+), 21 deletions(-) diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index b415f5d18..ce1543ba6 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -47,6 +47,8 @@ "vcmi.systemOptions.framerateButton.hover" : "Show FPS", "vcmi.systemOptions.framerateButton.help" : "{Show FPS}\n\n Toggles visibility of Frames Per Second counter in corner of game window.", + "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 info bar instead of showing up as popup windows", "vcmi.adventureOptions.numericQuantities.hover" : "Numeric Creature Quantities", "vcmi.adventureOptions.numericQuantities.help" : "{Numeric Creature Quantities}\n\n Shows approximate enemy creatures quantities in numeric A-B format.", "vcmi.adventureOptions.forceMovementInfo.hover" : "Always Show Movement Cost", diff --git a/Mods/vcmi/config/vcmi/russian.json b/Mods/vcmi/config/vcmi/russian.json index 0d2c54484..857ee7a18 100644 --- a/Mods/vcmi/config/vcmi/russian.json +++ b/Mods/vcmi/config/vcmi/russian.json @@ -47,6 +47,8 @@ "vcmi.systemOptions.framerateButton.hover" : "Показывать частоту кадров", "vcmi.systemOptions.framerateButton.help" : "{Показывать частоту кадров}\n\n Включить счетчик частоты кадров в углу игрового клиента", + "vcmi.adventureOptions.infoBarPick.hover" : "Сообщения в информационной панели", + "vcmi.adventureOptions.infoBarPick.help" : "{Сообщения в информационной панели}\n\n Если сообщения помещаются, то показывать их в информационной панели (только на интерфейсе карты).", "vcmi.adventureOptions.numericQuantities.hover" : "Приблизительное число существ", "vcmi.adventureOptions.numericQuantities.help" : "{Приблизительное число существ}\n\n Показывать приблизительное число существ в формате A-B вместо словесный обозначений", "vcmi.adventureOptions.forceMovementInfo.hover" : "Всегда показывать стоимость перемещения", diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 8d004ccd3..5c2ef92b8 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1034,11 +1034,18 @@ void CPlayerInterface::yourTacticPhase(int distance) void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &text, const std::vector & components, int soundID) { EVENT_HANDLER_CALLED_BY_CLIENT; - if(type == InfoWindow::INFO) { - adventureInt->infoBar->showComponents(components, text); - if (makingTurn && GH.listInt.size() && LOCPLINT == this) - CCS->soundh->playSound(static_cast(soundID)); - return; + + bool autoTryHover = settings["gameTweaks"]["infoBarPick"].Bool() && type == EInfoWindowMode::AUTO; + auto timer = type == EInfoWindowMode::INFO ? 3000 : 4500; //Implement long info windows like in HD mod + + if(autoTryHover || type == EInfoWindowMode::INFO) + { + if(adventureInt->infoBar->tryShowComponents(components, text, timer)) + { + if (makingTurn && GH.listInt.size() && LOCPLINT == this) + CCS->soundh->playSound(static_cast(soundID)); + return; + } } if (settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown()) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index 400eb7856..b30e1e31f 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -15,6 +15,7 @@ #include "../widgets/CComponent.h" #include "../widgets/Images.h" +#include "../windows/CMessage.h" #include "../widgets/TextControls.h" #include "../widgets/MiscWidgets.h" #include "../windows/InfoWindows.h" @@ -29,7 +30,7 @@ #include "../../lib/mapObjects/CGTownInstance.h" CInfoBar::CVisibleInfo::CVisibleInfo() - : CIntObject(0, Point(8, 12)) + : CIntObject(0, Point(offset_x, offset_y)) { } @@ -163,24 +164,31 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo() } } -CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector & compsToDisplay, std::string message) +CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector & compsToDisplay, std::string message, int textH, bool tiny) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATOT", 1, 0); - auto fullRect = Rect(4, 4, 171, 171); + auto fullRect = Rect(0, 0, data_width, data_height); auto textRect = fullRect; auto imageRect = fullRect; + auto font = FONT_SMALL; if(!compsToDisplay.empty()) { auto size = CComponent::large; if(compsToDisplay.size() > 2) size = CComponent::small; - if(message != "") + if(!message.empty()) { - textRect = Rect(4, 4, 171, 42); - imageRect = Rect(4, 42, 171, 121); + textRect = Rect(CInfoBar::offset, + CInfoBar::offset, + data_width - 2 * CInfoBar::offset, + textH); + imageRect = Rect(CInfoBar::offset, + textH + CInfoBar::offset, + data_width - 2 * CInfoBar::offset, + CInfoBar::data_height - 2* CInfoBar::offset - textH); if(compsToDisplay.size() > 4) size = CComponent::tiny; } @@ -194,8 +202,24 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector(vect, imageRect, 4, 4, 1); } + else + font = tiny ? FONT_TINY : font; - text = std::make_shared(message, textRect, 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); + if(!message.empty()) + text = std::make_shared(message, textRect, 0, font, ETextAlignment::CENTER, Colors::WHITE); +} + +int CInfoBar::getEstimatedComponentHeight(int numComps) const +{ + if (numComps > 8) //Bigger than 8 components - return invalid value + return std::numeric_limits::max(); + else if (numComps > 4) + return 48 + 20; // 24px * 2 rows + 20 to offset + else if (numComps > 2) + return 32 + 20; // 32px * 1 row + 20 to offset + else if (numComps) + return 128; // 128 px to offset + return 0; } void CInfoBar::playNewDaySound() @@ -293,13 +317,36 @@ void CInfoBar::showDate() redraw(); } -void CInfoBar::showComponents(const std::vector & comps, std::string message) +bool CInfoBar::tryShowComponents(const std::vector & components, std::string message, int timer) +{ + auto imageH = getEstimatedComponentHeight(components.size()) + (components.empty() ? 0 : 2 * CInfoBar::offset); + auto textH = CMessage::guessHeight(message,CInfoBar::data_width - 2 * CInfoBar::offset, FONT_SMALL); + auto tinyH = CMessage::guessHeight(message,CInfoBar::data_width - 2 * CInfoBar::offset, FONT_TINY); + auto header = CMessage::guessHeader(message); + auto headerH = CMessage::guessHeight(header, CInfoBar::data_width - 2 * CInfoBar::offset, FONT_SMALL); + + // Order matters - priority form should be chosen first + if(imageH + textH < CInfoBar::data_height) + showComponents(components, message, textH, false, timer); + else if(!imageH && tinyH < CInfoBar::data_height) + showComponents(components, message, tinyH, true, timer); + else if(imageH + headerH < CInfoBar::data_height) + showComponents(components, header, headerH, false, timer); + else if(imageH < CInfoBar::data_height) + showComponents(components, "", 0, false, timer); + else + return false; //We cannot fit message to infobar, fallback to window + + return true; +} + +void CInfoBar::showComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); state = COMPONENT; - visibleInfo = std::make_shared(comps, message); + visibleInfo = std::make_shared(comps, message, textH, tiny); - setTimer(3000); + setTimer(timer); redraw(); } diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 6c6ea662d..f55a43ec4 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -37,12 +37,15 @@ private: /// Declare before to compute correct size of widgets static constexpr int width = 192; static constexpr int height = 192; - static constexpr int component_offset = 8; + static constexpr int offset = 4; //all visible information located in one object - for ease of replacing class CVisibleInfo : public CIntObject { public: + static constexpr int offset_x = 8; + static constexpr int offset_y = 12; + void show(SDL_Surface * to) override; protected: @@ -52,6 +55,9 @@ private: CVisibleInfo(); }; + static constexpr int data_width = width - 2 * CVisibleInfo::offset_x; + static constexpr int data_height = height - 2 * CVisibleInfo::offset_y + 4; //yes, +4 is required + class EmptyVisibleInfo : public CVisibleInfo { public: @@ -108,7 +114,7 @@ private: std::shared_ptr comps; std::shared_ptr text; public: - VisibleComponentInfo(const std::vector & compsToDisplay, std::string message); + VisibleComponentInfo(const std::vector & compsToDisplay, std::string message, int textH, bool tiny); }; enum EState @@ -119,6 +125,9 @@ private: std::shared_ptr visibleInfo; EState state; + //private helper for showing components + void showComponents(const std::vector & comps, std::string message, int textH, bool tiny = false, int timer = 3000); + //removes all information about current state, deactivates timer (if any) void reset(); @@ -137,7 +146,7 @@ public: void showDate(); /// show components for 3 seconds. Used to display picked up resources. Can display up to 8 components - void showComponents(const std::vector & comps, std::string message); + bool tryShowComponents(const std::vector & comps, std::string message, int timer = 3000); /// print enemy turn progress void startEnemyTurn(PlayerColor color); @@ -154,5 +163,8 @@ public: /// check if infobar is showed something about pickups bool showingComponents(); + + /// get estimated component height for InfoBar + int getEstimatedComponentHeight(int numComps) const; }; diff --git a/client/windows/CMessage.cpp b/client/windows/CMessage.cpp index 48903dfb6..fcd7cc559 100644 --- a/client/windows/CMessage.cpp +++ b/client/windows/CMessage.cpp @@ -202,6 +202,25 @@ std::vector CMessage::breakText( std::string text, size_t maxLineWi return ret; } +std::string CMessage::guessHeader(const std::string & msg) +{ + size_t begin = 0; + std::string delimeters = "{}"; + size_t start = msg.find_first_of(delimeters[0], begin); + size_t end = msg.find_first_of(delimeters[1], start); + if(start > msg.size() || end > msg.size()) + return ""; + return msg.substr(begin, end); +} + +int CMessage::guessHeight(const std::string & txt, int width, EFonts font) +{ + const auto f = graphics->fonts[font]; + auto lines = CMessage::breakText(txt, width, font); + int lineHeight = static_cast(f->getLineHeight()); + return lineHeight * (int)lines.size(); +} + void CMessage::drawIWindow(CInfoWindow * ret, std::string text, PlayerColor player) { bool blitOr = false; diff --git a/client/windows/CMessage.h b/client/windows/CMessage.h index f41c5d01a..4ad5e36a1 100644 --- a/client/windows/CMessage.h +++ b/client/windows/CMessage.h @@ -32,6 +32,12 @@ public: /// split text in lines static std::vector breakText(std::string text, size_t maxLineWidth, EFonts font); + /// Try to guess a header of a message + static std::string guessHeader(const std::string & msg); + + /// For convenience + static int guessHeight(const std::string & string, int width, EFonts fnt); + /// constructor static void init(); /// destructor diff --git a/client/windows/settings/AdventureOptionsTab.cpp b/client/windows/settings/AdventureOptionsTab.cpp index 29d8dd074..cd66d3f79 100644 --- a/client/windows/settings/AdventureOptionsTab.cpp +++ b/client/windows/settings/AdventureOptionsTab.cpp @@ -105,11 +105,15 @@ AdventureOptionsTab::AdventureOptionsTab() addCallback("mapSwipeChanged", [](bool value) { #if defined(VCMI_MOBILE) - setBoolSetting("general", "swipe", value); + return setBoolSetting("general", "swipe", value); #else - setBoolSetting("general", "swipeDesktop", value); + return setBoolSetting("general", "swipeDesktop", value); #endif }); + addCallback("infoBarPickChanged", [](bool value) + { + return setBoolSetting("gameTweaks", "infoBarPick", value); + }); build(config); std::shared_ptr playerHeroSpeedToggle = widget("heroMovementSpeedPicker"); @@ -142,4 +146,6 @@ AdventureOptionsTab::AdventureOptionsTab() #else mapSwipeCheckbox->setSelected(settings["general"]["swipeDesktop"].Bool()); #endif + std::shared_ptr infoBarPickCheckbox = widget("infoBarPickCheckbox"); + infoBarPickCheckbox->setSelected(settings["gameTweaks"]["infoBarPick"].Bool()); } diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 378452cbd..651e095c9 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -503,6 +503,7 @@ "numericCreaturesQuantities", "availableCreaturesAsDwellingLabel", "compactTownCreatureInfo", + "infoBarPick", "skipBattleIntroMusic" ], "properties": { @@ -526,6 +527,10 @@ "type" : "boolean", "default" : false }, + "infoBarPick" : { + "type" : "boolean", + "default" : false + }, "skipBattleIntroMusic" : { "type" : "boolean", "default" : false diff --git a/config/widgets/settings/adventureOptionsTab.json b/config/widgets/settings/adventureOptionsTab.json index cab56ffa5..1184d9069 100644 --- a/config/widgets/settings/adventureOptionsTab.json +++ b/config/widgets/settings/adventureOptionsTab.json @@ -364,6 +364,10 @@ { "position": {"x": 45, "y": 385}, "text": "vcmi.adventureOptions.mapSwipe.hover" + }, + { + "position": {"x": 45, "y": 415}, + "text": "vcmi.adventureOptions.infoBarPick.hover" } ] }, @@ -399,6 +403,13 @@ "position": {"x": 10, "y": 383}, "callback": "mapSwipeChanged" }, - + { + "name": "infoBarPickCheckbox", + "type": "toggleButton", + "image": "sysopchk.def", + "help": "vcmi.adventureOptions.infoBarPick", + "position": {"x": 10, "y": 413}, + "callback": "infoBarPickChanged" + } ] } From a6cb7fd192a85f653b6dc111407facdcfba640f5 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 00:18:35 +0300 Subject: [PATCH 12/25] vcmi: allows to more than 8 components in infobar --- client/CPlayerInterface.cpp | 11 +++-- client/adventureMap/CAdvMapInt.cpp | 2 + client/adventureMap/CInfoBar.cpp | 69 ++++++++++++++++++++++-------- client/adventureMap/CInfoBar.h | 12 +++++- 4 files changed, 68 insertions(+), 26 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 5c2ef92b8..dcb1ff938 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1040,12 +1040,11 @@ void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &t if(autoTryHover || type == EInfoWindowMode::INFO) { - if(adventureInt->infoBar->tryShowComponents(components, text, timer)) - { - if (makingTurn && GH.listInt.size() && LOCPLINT == this) - CCS->soundh->playSound(static_cast(soundID)); - return; - } + adventureInt->infoBar->pushComponents(components, text, timer); + + if (makingTurn && GH.listInt.size() && LOCPLINT == this) + CCS->soundh->playSound(static_cast(soundID)); + return; } if (settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown()) diff --git a/client/adventureMap/CAdvMapInt.cpp b/client/adventureMap/CAdvMapInt.cpp index 2f3b820a2..b33697bd3 100644 --- a/client/adventureMap/CAdvMapInt.cpp +++ b/client/adventureMap/CAdvMapInt.cpp @@ -869,6 +869,8 @@ boost::optional CAdvMapInt::keyToMoveDirection(const SDL_Keycode & key) void CAdvMapInt::select(const CArmedInstance *sel, bool centerView) { assert(sel); + if(selection != sel) + infoBar->popAll(); selection = sel; mapAudio->onSelectionChanged(sel); if(centerView) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index b30e1e31f..fcc5d040b 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -178,7 +178,10 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector 2) + { size = CComponent::small; + font = FONT_TINY; + } if(!message.empty()) { textRect = Rect(CInfoBar::offset, @@ -192,13 +195,11 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector 4) size = CComponent::tiny; } - else if(compsToDisplay.size() > 4) - size = CComponent::small; std::vector> vect; for(const auto & c : compsToDisplay) - vect.emplace_back(std::make_shared(c, size)); + vect.emplace_back(std::make_shared(c, size, font)); comps = std::make_shared(vect, imageRect, 4, 4, 1); } @@ -263,7 +264,7 @@ void CInfoBar::tick() { removeUsedEvents(TIME); if(GH.topInt() == adventureInt) - showSelection(); + popComponents(); } void CInfoBar::clickLeft(tribool down, bool previousState) @@ -275,7 +276,7 @@ void CInfoBar::clickLeft(tribool down, bool previousState) else if(state == GAME) showDate(); else - showSelection(); + popComponents(); } } @@ -317,7 +318,24 @@ void CInfoBar::showDate() redraw(); } -bool CInfoBar::tryShowComponents(const std::vector & components, std::string message, int timer) +void CInfoBar::pushComponents(const std::vector & components, std::string message, int timer) +{ + if(components.empty()) + prepareComponents(components, message, timer); + else + { + std::vector vect = components; //I do not know currently how to avoid copy here + while(!vect.empty()) + { + std::vector sender = {vect.begin(), vect.begin() + std::min(vect.size(), 8ul)}; + prepareComponents(sender, message, timer); + vect.erase(vect.begin(), vect.begin() + std::min(vect.size(), 8ul)); + } + } + popComponents(); +} + +void CInfoBar::prepareComponents(const std::vector & components, std::string message, int timer) { auto imageH = getEstimatedComponentHeight(components.size()) + (components.empty() ? 0 : 2 * CInfoBar::offset); auto textH = CMessage::guessHeight(message,CInfoBar::data_width - 2 * CInfoBar::offset, FONT_SMALL); @@ -327,27 +345,42 @@ bool CInfoBar::tryShowComponents(const std::vector & components, std: // Order matters - priority form should be chosen first if(imageH + textH < CInfoBar::data_height) - showComponents(components, message, textH, false, timer); + pushComponents(components, message, textH, false, timer); else if(!imageH && tinyH < CInfoBar::data_height) - showComponents(components, message, tinyH, true, timer); + pushComponents(components, message, tinyH, true, timer); else if(imageH + headerH < CInfoBar::data_height) - showComponents(components, header, headerH, false, timer); - else if(imageH < CInfoBar::data_height) - showComponents(components, "", 0, false, timer); + pushComponents(components, header, headerH, false, timer); else - return false; //We cannot fit message to infobar, fallback to window + pushComponents(components, "", 0, false, timer); - return true; + return; } -void CInfoBar::showComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer) +void CInfoBar::popAll() +{ + componentsQueue = {}; +} + +void CInfoBar::popComponents() { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); - state = COMPONENT; - visibleInfo = std::make_shared(comps, message, textH, tiny); + if(!componentsQueue.empty()) + { + state = COMPONENT; + const auto & extracted = componentsQueue.front(); + visibleInfo = extracted.first; + setTimer(extracted.second); + componentsQueue.pop(); + redraw(); + return; + } + showSelection(); +} - setTimer(timer); - redraw(); +void CInfoBar::pushComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer) +{ + OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); + componentsQueue.emplace(std::make_shared(comps, message, textH, tiny), timer); } bool CInfoBar::showingComponents() diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index f55a43ec4..09c214fe6 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -125,8 +125,13 @@ private: std::shared_ptr visibleInfo; EState state; + std::queue, int>> componentsQueue; + //private helper for showing components - void showComponents(const std::vector & comps, std::string message, int textH, bool tiny = false, int timer = 3000); + void showComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer); + void pushComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer); + void prepareComponents(const std::vector & comps, std::string message, int timer); + void popComponents(); //removes all information about current state, deactivates timer (if any) void reset(); @@ -146,7 +151,10 @@ public: void showDate(); /// show components for 3 seconds. Used to display picked up resources. Can display up to 8 components - bool tryShowComponents(const std::vector & comps, std::string message, int timer = 3000); + void pushComponents(const std::vector & comps, std::string message, int timer = 3000); + + /// Remove all queued components + void popAll(); /// print enemy turn progress void startEnemyTurn(PlayerColor color); From 8edba4fb06e3e9bbabb6467aac9444ed9496305d Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 7 Mar 2023 04:09:19 +0300 Subject: [PATCH 13/25] vcmi: use MODAL by default Convert only some objects to AUTO, need futher testing than no non-adventure objects will not use AUTO or INFO --- lib/mapObjects/CBank.cpp | 1 + lib/mapObjects/CGHeroInstance.cpp | 1 + lib/mapObjects/CGPandoraBox.cpp | 3 +++ lib/mapObjects/CGTownInstance.cpp | 4 ++++ lib/mapObjects/MiscObjects.cpp | 13 +++++++++++-- server/CGameHandler.cpp | 1 + 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index c517ef65d..777784bbd 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -132,6 +132,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const { int textID = -1; InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = hero->getOwner(); MetaString loot; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 2ded12419..a55f755ef 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -876,6 +876,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.soundID = soundBase::pickup01 + rand.nextInt(6); iw.player = tempOwner; iw.components.emplace_back(raisedStack); diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 7dc43b918..37e9d8340 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -29,6 +29,7 @@ VCMI_LIB_NAMESPACE_BEGIN static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID) { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.soundID = soundID; iw.player = playerID; iw.text.addTxt(MetaString::ADVOB_TXT,txtID); @@ -60,6 +61,7 @@ void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const afterSuccessfulVisit(); InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->getOwner(); bool changesPrimSkill = false; @@ -139,6 +141,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const std::string msg = message; //in case box is removed in the meantime InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->getOwner(); //TODO: reuse this code for Scholar skill diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index dd2dc87fb..5d87f5011 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -173,6 +173,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const if(ID == Obj::REFUGEE_CAMP && !creatures[0].first) //Refugee Camp, no available cres { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; iw.text.addTxt(MetaString::ADVOB_TXT, 44); //{%s} \n\n The camp is deserted. Perhaps you should try next week. iw.text.addReplacement(MetaString::OBJ_NAMES, ID); @@ -328,6 +329,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const if(!slot.validSlot()) //no available slot { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 425);//The %s would join your hero, but there aren't enough provisions to support them. iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid); @@ -342,6 +344,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 423); //%d %s join your army. iw.text.addReplacement(count); @@ -355,6 +358,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const else //there no creatures { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.text.addTxt(MetaString::GENERAL_TXT, 422); //There are no %s here to recruit. iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid); iw.player = h->tempOwner; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 5f48f8ebf..15370e782 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -46,6 +46,7 @@ static void openWindow(const OpenWindow::EWindow type, const int id1, const int static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID = 0) { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; if(soundID) iw.soundID = soundID; iw.player = playerID; @@ -740,6 +741,7 @@ void CGMine::flagMine(const PlayerColor & player) const cb->setOwner(this, player); //not ours? flag it! InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.soundID = soundBase::FLAGMINE; iw.text.addTxt(MetaString::MINE_EVNTS,producedResource); //not use subID, abandoned mines uses default mine texts iw.player = player; @@ -901,8 +903,6 @@ void CGResource::collectRes(const PlayerColor & player) const sii.text.addReplacement(MetaString::RES_NAMES, subID); } sii.components.emplace_back(Component::RESOURCE,subID,amount,0); - sii.text.addTxt(MetaString::ADVOB_TXT,113); - sii.text.addReplacement(MetaString::RES_NAMES, subID); sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6); cb->showInfoDialog(&sii); cb->removeObject(this); @@ -1239,6 +1239,7 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const vstd::amax(countToTake, 1); InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; iw.text.addTxt(MetaString::ADVOB_TXT, 168); iw.components.emplace_back(CStackBasicDescriptor(h->getCreature(targetstack), countToTake)); @@ -1321,6 +1322,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const if(!stacksCount()) { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; switch(ID) { @@ -1449,6 +1451,7 @@ void CGWitchHut::initObj(CRandomGenerator & rand) void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->getOwner(); if(!wasVisited(h->tempOwner)) cb->setObjProperty(id, CGWitchHut::OBJPROP_VISITED, h->tempOwner.getNum()); @@ -1523,6 +1526,7 @@ void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler) void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; switch (ID) { @@ -1565,6 +1569,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const cb->setObjProperty(id, CGShrine::OBJPROP_VISITED, h->tempOwner.getNum()); InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->getOwner(); iw.text.addTxt(MetaString::ADVOB_TXT,127 + ID - 88); iw.text.addTxt(MetaString::SPELL_NAME,spell); @@ -1682,6 +1687,7 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const } InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->getOwner(); iw.text.addTxt(MetaString::ADVOB_TXT,115); @@ -1909,6 +1915,7 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const iw.player = h->tempOwner; if(h->hasBonusFrom(Bonus::OBJECT,ID)) //has already visited Sirens { + iw.type = EInfoWindowMode::AUTO; iw.text.addTxt(MetaString::ADVOB_TXT,133); } else @@ -1974,6 +1981,7 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const if(s != IBoatGenerator::GOOD) { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = tempOwner; getProblemText(iw.text, h); cb->showInfoDialog(&iw); @@ -2070,6 +2078,7 @@ void CGDenOfthieves::onHeroVisit (const CGHeroInstance * h) const void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const { InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; TeamState *ts = cb->gameState()->getPlayerTeam(h->tempOwner); assert(ts); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4534b1618..ef99ad7df 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -6046,6 +6046,7 @@ bool CGameHandler::dig(const CGHeroInstance *h) sendAndApply(&smp); InfoWindow iw; + iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; if (gs->map->grailPos == h->visitablePos()) { From 716dd9a43bd9c6cda3b0f6f479038cd36bcd8bf0 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Wed, 8 Mar 2023 01:32:21 +0300 Subject: [PATCH 14/25] vcmi: remove a bunch of duplicated showInfoDialog These methods duplicated in almost every map object Just replace it by one such method --- client/NetPacksClient.cpp | 20 ++++++------- lib/NetPacks.h | 7 +---- lib/NetPacksBase.h | 14 +++++++++ lib/mapObjects/CGHeroInstance.cpp | 19 +----------- lib/mapObjects/CGMarket.cpp | 14 ++------- lib/mapObjects/CGPandoraBox.cpp | 21 ++----------- lib/mapObjects/CGTownInstance.cpp | 4 +-- lib/mapObjects/CObjectHandler.cpp | 25 +++++----------- lib/mapObjects/CObjectHandler.h | 9 +++++- lib/mapObjects/CQuest.cpp | 22 ++------------ lib/mapObjects/MiscObjects.cpp | 49 ++++++++----------------------- server/CGameHandler.cpp | 2 +- 12 files changed, 63 insertions(+), 143 deletions(-) diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index dee2ebf71..e9e859018 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -875,28 +875,28 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack) { switch(pack.window) { - case OpenWindow::RECRUITMENT_FIRST: - case OpenWindow::RECRUITMENT_ALL: + case EOpenWindowMode::RECRUITMENT_FIRST: + case EOpenWindowMode::RECRUITMENT_ALL: { const CGDwelling *dw = dynamic_cast(cl.getObj(ObjectInstanceID(pack.id1))); const CArmedInstance *dst = dynamic_cast(cl.getObj(ObjectInstanceID(pack.id2))); - callInterfaceIfPresent(cl, dst->tempOwner, &IGameEventsReceiver::showRecruitmentDialog, dw, dst, pack.window == OpenWindow::RECRUITMENT_FIRST ? 0 : -1); + callInterfaceIfPresent(cl, dst->tempOwner, &IGameEventsReceiver::showRecruitmentDialog, dw, dst, pack.window == EOpenWindowMode::RECRUITMENT_FIRST ? 0 : -1); } break; - case OpenWindow::SHIPYARD_WINDOW: + case EOpenWindowMode::SHIPYARD_WINDOW: { const IShipyard *sy = IShipyard::castFrom(cl.getObj(ObjectInstanceID(pack.id1))); callInterfaceIfPresent(cl, sy->o->tempOwner, &IGameEventsReceiver::showShipyardDialog, sy); } break; - case OpenWindow::THIEVES_GUILD: + case EOpenWindowMode::THIEVES_GUILD: { //displays Thieves' Guild window (when hero enters Den of Thieves) const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.id2)); callInterfaceIfPresent(cl, PlayerColor(pack.id1), &IGameEventsReceiver::showThievesGuildWindow, obj); } break; - case OpenWindow::UNIVERSITY_WINDOW: + case EOpenWindowMode::UNIVERSITY_WINDOW: { //displays University window (when hero enters University on adventure map) const IMarket *market = IMarket::castFrom(cl.getObj(ObjectInstanceID(pack.id1))); @@ -904,7 +904,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack) callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showUniversityWindow, market, hero); } break; - case OpenWindow::MARKET_WINDOW: + case EOpenWindowMode::MARKET_WINDOW: { //displays Thieves' Guild window (when hero enters Den of Thieves) const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.id1)); @@ -913,7 +913,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack) callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero); } break; - case OpenWindow::HILL_FORT_WINDOW: + case EOpenWindowMode::HILL_FORT_WINDOW: { //displays Hill fort window const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.id1)); @@ -921,12 +921,12 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack) callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showHillFortWindow, obj, hero); } break; - case OpenWindow::PUZZLE_MAP: + case EOpenWindowMode::PUZZLE_MAP: { callInterfaceIfPresent(cl, PlayerColor(pack.id1), &IGameEventsReceiver::showPuzzleMap); } break; - case OpenWindow::TAVERN_WINDOW: + case EOpenWindowMode::TAVERN_WINDOW: const CGObjectInstance *obj1 = cl.getObj(ObjectInstanceID(pack.id1)), *obj2 = cl.getObj(ObjectInstanceID(pack.id2)); callInterfaceIfPresent(cl, obj1->tempOwner, &IGameEventsReceiver::showTavernWindow, obj2); diff --git a/lib/NetPacks.h b/lib/NetPacks.h index c1d32d263..ce078e20a 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -718,12 +718,7 @@ struct DLL_LINKAGE GiveHero : public CPackForClient struct DLL_LINKAGE OpenWindow : public CPackForClient { - enum EWindow - { - EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW, THIEVES_GUILD, - UNIVERSITY_WINDOW, HILL_FORT_WINDOW, MARKET_WINDOW, PUZZLE_MAP, TAVERN_WINDOW - }; - ui8 window; + EOpenWindowMode window; si32 id1 = -1; si32 id2 = -1; diff --git a/lib/NetPacksBase.h b/lib/NetPacksBase.h index 29c0d09fb..642d63638 100644 --- a/lib/NetPacksBase.h +++ b/lib/NetPacksBase.h @@ -42,6 +42,20 @@ enum class EInfoWindowMode : uint8_t INFO }; +enum class EOpenWindowMode : uint8_t +{ + EXCHANGE_WINDOW, + RECRUITMENT_FIRST, + RECRUITMENT_ALL, + SHIPYARD_WINDOW, + THIEVES_GUILD, + UNIVERSITY_WINDOW, + HILL_FORT_WINDOW, + MARKET_WINDOW, + PUZZLE_MAP, + TAVERN_WINDOW +}; + struct DLL_LINKAGE CPack { std::shared_ptr c; // Pointer to connection that pack received from diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index a55f755ef..d2f12c40c 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -36,23 +36,6 @@ VCMI_LIB_NAMESPACE_BEGIN - -///helpers -static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID = 0) -{ - InfoWindow iw; - iw.soundID = soundID; - iw.player = playerID; - iw.text.addTxt(MetaString::ADVOB_TXT,txtID); - IObjectInterface::cb->sendAndApply(&iw); -} - -static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) -{ - const PlayerColor playerID = h->getOwner(); - showInfoDialog(playerID,txtID,soundID); -} - static int lowestSpeed(const CGHeroInstance * chi) { static const CSelector selectorSTACKS_SPEED = Selector::type()(Bonus::STACKS_SPEED); @@ -462,7 +445,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const txt_id = 103; } - showInfoDialog(h,txt_id); + h->showInfoDialog(txt_id); } } diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index 82171e3f2..f711a3747 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -22,16 +22,6 @@ VCMI_LIB_NAMESPACE_BEGIN -///helpers -static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1) -{ - OpenWindow ow; - ow.window = type; - ow.id1 = id1; - ow.id2 = id2; - IObjectInterface::cb->sendAndApply(&ow); -} - bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMarketMode mode) const { switch(mode) @@ -205,7 +195,7 @@ std::vector IMarket::availableModes() const void CGMarket::onHeroVisit(const CGHeroInstance * h) const { - openWindow(OpenWindow::MARKET_WINDOW,id.getNum(),h->id.getNum()); + openWindow(EOpenWindowMode::MARKET_WINDOW,id.getNum(),h->id.getNum()); } int CGMarket::getMarketEfficiency() const @@ -339,7 +329,7 @@ std::vector CGUniversity::availableItemsIds(EMarketMode::EMarketMode mode) void CGUniversity::onHeroVisit(const CGHeroInstance * h) const { - openWindow(OpenWindow::UNIVERSITY_WINDOW,id.getNum(),h->id.getNum()); + openWindow(EOpenWindowMode::UNIVERSITY_WINDOW,id.getNum(),h->id.getNum()); } VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 37e9d8340..f5f652198 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -25,23 +25,6 @@ VCMI_LIB_NAMESPACE_BEGIN -///helpers -static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID) -{ - InfoWindow iw; - iw.type = EInfoWindowMode::AUTO; - iw.soundID = soundID; - iw.player = playerID; - iw.text.addTxt(MetaString::ADVOB_TXT,txtID); - IObjectInterface::cb->sendAndApply(&iw); -} - -static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID) -{ - const PlayerColor playerID = h->getOwner(); - showInfoDialog(playerID,txtID,soundID); -} - void CGPandoraBox::initObj(CRandomGenerator & rand) { blockVisit = (ID==Obj::PANDORAS_BOX); //block only if it's really pandora's box (events also derive from that class) @@ -342,7 +325,7 @@ void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answe { if(stacksCount() > 0) //if pandora's box is protected by army { - showInfoDialog(hero,16,0); + hero->showInfoDialog(16, 0, EInfoWindowMode::MODAL); cb->startBattleI(hero, this); //grants things after battle } else if(message.empty() && resources.empty() @@ -351,7 +334,7 @@ void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answe && spells.empty() && creatures.stacksCount() > 0 && gainedExp == 0 && manaDiff == 0 && moraleDiff == 0 && luckDiff == 0) //if it gives nothing without battle { - showInfoDialog(hero,15,0); + hero->showInfoDialog(15); cb->removeObject(this); } else //if it gives something without battle diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 5d87f5011..296747a18 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -383,8 +383,8 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const ow.id1 = id.getNum(); ow.id2 = h->id.getNum(); ow.window = (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::REFUGEE_CAMP) - ? OpenWindow::RECRUITMENT_FIRST - : OpenWindow::RECRUITMENT_ALL; + ? EOpenWindowMode::RECRUITMENT_FIRST + : EOpenWindowMode::RECRUITMENT_ALL; cb->sendAndApply(&ow); } } diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index a8560d9b2..f7658209e 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -32,7 +32,7 @@ VCMI_LIB_NAMESPACE_BEGIN IGameCallback * IObjectInterface::cb = nullptr; ///helpers -static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1) +void IObjectInterface::openWindow(const EOpenWindowMode type, const int id1, const int id2) { OpenWindow ow; ow.window = type; @@ -41,27 +41,16 @@ static void openWindow(const OpenWindow::EWindow type, const int id1, const int IObjectInterface::cb->sendAndApply(&ow); } -static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID) +void IObjectInterface::showInfoDialog(const ui32 txtID, const ui16 soundID, EInfoWindowMode mode) const { InfoWindow iw; iw.soundID = soundID; - iw.player = playerID; + iw.player = getOwner(); + iw.type = mode; iw.text.addTxt(MetaString::ADVOB_TXT,txtID); IObjectInterface::cb->sendAndApply(&iw); } -/*static void showInfoDialog(const ObjectInstanceID heroID, const ui32 txtID, const ui16 soundID) -{ - const PlayerColor playerID = IObjectInterface::cb->getOwner(heroID); - showInfoDialog(playerID,txtID,soundID); -}*/ - -static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) -{ - const PlayerColor playerID = h->getOwner(); - showInfoDialog(playerID,txtID,soundID); -} - ///IObjectInterface void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const {} @@ -340,18 +329,18 @@ void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const { case Obj::HILL_FORT: { - openWindow(OpenWindow::HILL_FORT_WINDOW,id.getNum(),h->id.getNum()); + openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum()); } break; case Obj::SANCTUARY: { //You enter the sanctuary and immediately feel as if a great weight has been lifted off your shoulders. You feel safe here. - showInfoDialog(h, 114); + h->showInfoDialog(114); } break; case Obj::TAVERN: { - openWindow(OpenWindow::TAVERN_WINDOW,h->id.getNum(),id.getNum()); + openWindow(EOpenWindowMode::TAVERN_WINDOW,h->id.getNum(),id.getNum()); } break; } diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index e5748dd05..f79fb3d79 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -13,6 +13,7 @@ #include "../int3.h" #include "../HeroBonus.h" +#include "../NetPacksBase.h" VCMI_LIB_NAMESPACE_BEGIN @@ -57,7 +58,13 @@ public: virtual void garrisonDialogClosed(const CGHeroInstance *hero) const; virtual void heroLevelUpDone(const CGHeroInstance *hero) const; -//unified interface, AI helpers + //unified helper to show info dialog for object owner + virtual void showInfoDialog(const ui32 txtID, const ui16 soundID = 0, EInfoWindowMode mode = EInfoWindowMode::AUTO) const; + + //unified helper to show a specific window + static void openWindow(const EOpenWindowMode type, const int id1, const int id2 = -1); + + //unified interface, AI helpers virtual bool wasVisited (PlayerColor player) const; virtual bool wasVisited (const CGHeroInstance * h) const; diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 6d22132fc..9388f7e35 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -50,22 +50,6 @@ CQuest::CQuest(): { } -///helpers -static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID = 0) -{ - InfoWindow iw; - iw.soundID = soundID; - iw.player = playerID; - iw.text.addTxt(MetaString::ADVOB_TXT,txtID); - IObjectInterface::cb->sendAndApply(&iw); -} - -static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) -{ - const PlayerColor playerID = h->getOwner(); - showInfoDialog(playerID,txtID,soundID); -} - static std::string visitedTxt(const bool visited) { int id = visited ? 352 : 353; @@ -1146,7 +1130,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const } else txt_id=20; - showInfoDialog(h, txt_id); + h->showInfoDialog(txt_id); } void CGBorderGuard::initObj(CRandomGenerator & rand) @@ -1182,7 +1166,7 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const } else { - showInfoDialog(h, 18); + h->showInfoDialog(18); AddQuest aq; aq.quest = QuestInfo (quest, this, visitablePos()); @@ -1207,7 +1191,7 @@ void CGBorderGate::onHeroVisit(const CGHeroInstance * h) const //TODO: passabili { if (!wasMyColorVisited (h->getOwner()) ) { - showInfoDialog(h,18,0); + h->showInfoDialog(18); AddQuest aq; aq.quest = QuestInfo (quest, this, visitablePos()); diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 15370e782..7380af760 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -34,32 +34,6 @@ ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map std::map CGObelisk::visited; //map: team_id => how many obelisks has been visited ///helpers -static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1) -{ - OpenWindow ow; - ow.window = type; - ow.id1 = id1; - ow.id2 = id2; - IObjectInterface::cb->sendAndApply(&ow); -} - -static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID = 0) -{ - InfoWindow iw; - iw.type = EInfoWindowMode::AUTO; - if(soundID) - iw.soundID = soundID; - iw.player = playerID; - iw.text.addTxt(MetaString::ADVOB_TXT,txtID); - IObjectInterface::cb->sendAndApply(&iw); -} - -static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) -{ - const PlayerColor playerID = h->getOwner(); - showInfoDialog(playerID,txtID,soundID); -} - static std::string visitedTxt(const bool visited) { int id = visited ? 352 : 353; @@ -386,7 +360,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co } else //they fight { - showInfoDialog(h,87,0);//Insulted by your refusal of their offer, the monsters attack! + h->showInfoDialog(87, 0, EInfoWindowMode::MODAL);//Insulted by your refusal of their offer, the monsters attack! fight(h); } } @@ -600,6 +574,7 @@ void CGCreature::giveReward(const CGHeroInstance * h) const if(!iw.components.empty()) { + iw.type = EInfoWindowMode::AUTO; iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure iw.text.addReplacement(h->getNameTranslated()); cb->showInfoDialog(&iw); @@ -769,7 +744,7 @@ void CGMine::battleFinished(const CGHeroInstance *hero, const BattleResult &resu { if(isAbandoned()) { - showInfoDialog(hero->tempOwner, 85, 0); + hero->showInfoDialog(85); } flagMine(hero->tempOwner); } @@ -1080,7 +1055,7 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const logGlobal->debug("All exits blocked for monolith %d at %s", id.getNum(), pos.toString()); } else - showInfoDialog(h, 70, 0); + h->showInfoDialog(70); cb->showTeleportDialog(&td); } @@ -1136,7 +1111,7 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const TeleportDialog td(h->tempOwner, channel); if(cb->isTeleportChannelImpassable(channel)) { - showInfoDialog(h,153,0);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged. + h->showInfoDialog(153);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged. logGlobal->debug("Cannot find exit subterranean gate for %d at %s", id.getNum(), pos.toString()); td.impassable = true; } @@ -1860,7 +1835,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const { if (ID == Obj::HUT_OF_MAGI) { - showInfoDialog(h, 61); + h->showInfoDialog(61); if (!eyelist[subID].empty()) { @@ -1890,7 +1865,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const } else if (ID == Obj::EYE_OF_MAGI) { - showInfoDialog(h, 48); + h->showInfoDialog(48); } } @@ -1988,7 +1963,7 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const } else { - openWindow(OpenWindow::SHIPYARD_WINDOW,id.getNum(),h->id.getNum()); + openWindow(EOpenWindowMode::SHIPYARD_WINDOW,id.getNum(),h->id.getNum()); } } @@ -2028,12 +2003,12 @@ void CCartographer::onHeroVisit( const CGHeroInstance * h ) const } else //if he cannot afford { - showInfoDialog(h, 28); + h->showInfoDialog(28); } } else //if he already visited carographer { - showInfoDialog(h, 24); + h->showInfoDialog(24); } } @@ -2092,7 +2067,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const // increment general visited obelisks counter cb->setObjProperty(id, CGObelisk::OBJPROP_INC, team.getNum()); - openWindow(OpenWindow::PUZZLE_MAP, h->tempOwner.getNum()); + openWindow(EOpenWindowMode::PUZZLE_MAP, h->tempOwner.getNum()); // mark that particular obelisk as visited for all players in the team for(const auto & color : ts->players) @@ -2153,7 +2128,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const { PlayerColor oldOwner = tempOwner; cb->setOwner(this,h->tempOwner); //not ours? flag it! - showInfoDialog(h, 69); + h->showInfoDialog(69); giveBonusTo(h->tempOwner); if(oldOwner < PlayerColor::PLAYER_LIMIT) //remove bonus from old owner diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ef99ad7df..59d6c5edf 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5721,7 +5721,7 @@ void CGameHandler::showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID h void CGameHandler::showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) { OpenWindow ow; - ow.window = OpenWindow::THIEVES_GUILD; + ow.window = EOpenWindowMode::THIEVES_GUILD; ow.id1 = player.getNum(); ow.id2 = requestingObjId.getNum(); sendAndApply(&ow); From 8a05f5bed746a3144338f2648c4928ccd5f4bdae Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 01:26:44 +0300 Subject: [PATCH 15/25] vcmi: allow set font size on component Now it is possible to set arbitrary font size for CComponent. So, let's use it --- client/widgets/CComponent.cpp | 18 ++++++++++-------- client/widgets/CComponent.h | 7 ++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 5505e4158..5a860dfaa 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -33,22 +33,22 @@ #include "../../lib/NetPacksBase.h" #include "../../lib/CArtHandler.h" -CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize) - : perDay(false) +CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font): + perDay(false) { - init(Type, Subtype, Val, imageSize); + init(Type, Subtype, Val, imageSize, font); } -CComponent::CComponent(const Component & c, ESize imageSize) +CComponent::CComponent(const Component & c, ESize imageSize, EFonts font) : perDay(false) { if(c.id == Component::RESOURCE && c.when==-1) perDay = true; - init((Etype)c.id, c.subtype, c.val, imageSize); + init((Etype)c.id, c.subtype, c.val, imageSize, font); } -void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize) +void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts fnt) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -58,6 +58,7 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize) subtype = Subtype; val = Val; size = imageSize; + font = fnt; assert(compType < typeInvalid); assert(size < sizeInvalid); @@ -67,13 +68,14 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize) pos.w = image->pos.w; pos.h = image->pos.h; - EFonts font = FONT_SMALL; if (imageSize < small) - font = FONT_TINY; //other sizes? + font = FONT_TINY; //for tiny images - tiny font pos.h += 4; //distance between text and image auto max = 80; + if (size < large) + max = 60; if (size < medium) max = 40; if (size < small) diff --git a/client/widgets/CComponent.h b/client/widgets/CComponent.h index b56453f99..99494e805 100644 --- a/client/widgets/CComponent.h +++ b/client/widgets/CComponent.h @@ -47,13 +47,14 @@ private: void setSurface(std::string defName, int imgPos); std::string getSubtitleInternal(); - void init(Etype Type, int Subtype, int Val, ESize imageSize); + void init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font = FONT_SMALL); public: std::shared_ptr image; Etype compType; //component type ESize size; //component size. + EFonts font; //Font size of label int subtype; //type-dependant subtype. See getSomething methods for details int val; // value \ strength \ amount of component. See getSomething methods for details bool perDay; // add "per day" text to subtitle @@ -61,8 +62,8 @@ public: std::string getDescription(); std::string getSubtitle(); - CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large); - CComponent(const Component &c, ESize imageSize=large); + CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large, EFonts font = FONT_SMALL); + CComponent(const Component &c, ESize imageSize=large, EFonts font = FONT_SMALL); void clickRight(tribool down, bool previousState) override; //call-in }; From 1ca89c849235cf83388ca6318d5a4f5b7364ea4c Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 02:58:16 +0300 Subject: [PATCH 16/25] vcmi: implement splitting by ID in infobar This will get a much better result than naive sorting --- client/adventureMap/CInfoBar.cpp | 34 ++++++++++++++++++++++---------- client/adventureMap/CInfoBar.h | 16 ++++++++++++++- client/widgets/CComponent.cpp | 2 +- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index fcc5d040b..5a486224b 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -179,7 +179,7 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector 2) { - size = CComponent::small; + size = CComponent::medium; font = FONT_TINY; } if(!message.empty()) @@ -195,6 +195,8 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector 4) size = CComponent::tiny; } + if(compsToDisplay.size() > 6) + size = CComponent::small; std::vector> vect; @@ -217,7 +219,7 @@ int CInfoBar::getEstimatedComponentHeight(int numComps) const else if (numComps > 4) return 48 + 20; // 24px * 2 rows + 20 to offset else if (numComps > 2) - return 32 + 20; // 32px * 1 row + 20 to offset + return 96 + 20; // 32px * 1 row + 20 to offset else if (numComps) return 128; // 128 px to offset return 0; @@ -318,18 +320,30 @@ void CInfoBar::showDate() redraw(); } + void CInfoBar::pushComponents(const std::vector & components, std::string message, int timer) { + auto actualPush = [&](const std::vector & components, std::string message, int timer, size_t max){ + std::vector vect = components; //I do not know currently how to avoid copy here + while(!vect.empty()) + { + std::vector sender = {vect.begin(), vect.begin() + std::min(vect.size(), max)}; + prepareComponents(sender, message, timer); + vect.erase(vect.begin(), vect.begin() + std::min(vect.size(), max)); + }; + }; if(components.empty()) prepareComponents(components, message, timer); else { - std::vector vect = components; //I do not know currently how to avoid copy here - while(!vect.empty()) + std::map> reward_map; + for(const auto & c : components) + reward_map[static_cast(c.id)].push_back(c); + + for(const auto & kv : reward_map) { - std::vector sender = {vect.begin(), vect.begin() + std::min(vect.size(), 8ul)}; - prepareComponents(sender, message, timer); - vect.erase(vect.begin(), vect.begin() + std::min(vect.size(), 8ul)); + auto vector = kv.second; + actualPush(kv.second, message, timer, kv.first == Component::ARTIFACT ? 6 : 8); } } popComponents(); @@ -368,9 +382,9 @@ void CInfoBar::popComponents() { state = COMPONENT; const auto & extracted = componentsQueue.front(); - visibleInfo = extracted.first; - setTimer(extracted.second); + visibleInfo = std::make_shared(extracted.first); componentsQueue.pop(); + setTimer(extracted.second); redraw(); return; } @@ -380,7 +394,7 @@ void CInfoBar::popComponents() void CInfoBar::pushComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); - componentsQueue.emplace(std::make_shared(comps, message, textH, tiny), timer); + componentsQueue.emplace(VisibleComponentInfo::Cache(comps, message, textH, tiny), timer); } bool CInfoBar::showingComponents() diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 09c214fe6..4b7e31497 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -114,6 +114,20 @@ private: std::shared_ptr comps; std::shared_ptr text; public: + struct Cache + { + std::vector compsToDisplay; + std::string message; + int textH; + bool tiny; + Cache(std::vector comps, std::string msg, int textH, bool tiny): + compsToDisplay(std::move(comps)), + message(std::move(msg)), + textH(textH), + tiny(tiny) + {} + }; + VisibleComponentInfo(const Cache & c): VisibleComponentInfo(c.compsToDisplay, c.message, c.textH, c.tiny) {} VisibleComponentInfo(const std::vector & compsToDisplay, std::string message, int textH, bool tiny); }; @@ -125,7 +139,7 @@ private: std::shared_ptr visibleInfo; EState state; - std::queue, int>> componentsQueue; + std::queue> componentsQueue; //private helper for showing components void showComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer); diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 5a860dfaa..bb281c6f7 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -101,7 +101,7 @@ const std::vector CComponent::getFileName() { static const std::string primSkillsArr [] = {"PSKIL32", "PSKIL32", "PSKIL42", "PSKILL"}; static const std::string secSkillsArr [] = {"SECSK32", "SECSK32", "SECSKILL", "SECSK82"}; - static const std::string resourceArr [] = {"SMALRES", "RESOURCE", "RESOUR82", "RESOUR82"}; + static const std::string resourceArr [] = {"SMALRES", "RESOURCE", "RESOURCE", "RESOUR82"}; static const std::string creatureArr [] = {"CPRSMALL", "CPRSMALL", "TWCRPORT", "TWCRPORT"}; static const std::string artifactArr[] = {"Artifact", "Artifact", "Artifact", "Artifact"}; static const std::string spellsArr [] = {"SpellInt", "SpellInt", "SPELLSCR", "SPELLSCR"}; From 2bd74e5c67b46b36010b80b1087a8ba3bbbafc92 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 13:40:26 +0300 Subject: [PATCH 17/25] vcmi: use small icons for medium components It makes more sense due to current UI implementation. It is better to have icon smaller than have icons overlap --- client/widgets/CComponent.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index bb281c6f7..1873e45ec 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -102,12 +102,12 @@ const std::vector CComponent::getFileName() static const std::string primSkillsArr [] = {"PSKIL32", "PSKIL32", "PSKIL42", "PSKILL"}; static const std::string secSkillsArr [] = {"SECSK32", "SECSK32", "SECSKILL", "SECSK82"}; static const std::string resourceArr [] = {"SMALRES", "RESOURCE", "RESOURCE", "RESOUR82"}; - static const std::string creatureArr [] = {"CPRSMALL", "CPRSMALL", "TWCRPORT", "TWCRPORT"}; + static const std::string creatureArr [] = {"CPRSMALL", "CPRSMALL", "CPRSMALL", "TWCRPORT"}; static const std::string artifactArr[] = {"Artifact", "Artifact", "Artifact", "Artifact"}; - static const std::string spellsArr [] = {"SpellInt", "SpellInt", "SPELLSCR", "SPELLSCR"}; + static const std::string spellsArr [] = {"SpellInt", "SpellInt", "SpellInt", "SPELLSCR"}; static const std::string moraleArr [] = {"IMRL22", "IMRL30", "IMRL42", "imrl82"}; static const std::string luckArr [] = {"ILCK22", "ILCK30", "ILCK42", "ilck82"}; - static const std::string heroArr [] = {"PortraitsSmall", "PortraitsSmall", "PortraitsLarge", "PortraitsLarge"}; + static const std::string heroArr [] = {"PortraitsSmall", "PortraitsSmall", "PortraitsSmall", "PortraitsLarge"}; static const std::string flagArr [] = {"CREST58", "CREST58", "CREST58", "CREST58"}; auto gen = [](const std::string * arr) From bc228a938a3fa06c395d262156e3b07d48a70e65 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 15:54:12 +0300 Subject: [PATCH 18/25] vcmi: use enum class for EComponentType There is really no reason not to use it --- AI/Nullkiller/AIGateway.cpp | 2 +- client/CPlayerInterface.cpp | 2 +- client/adventureMap/CInfoBar.cpp | 58 ++++++++++++++++++++--- client/widgets/CComponent.cpp | 2 +- lib/NetPacksBase.h | 20 +++++++- lib/NetPacksLib.cpp | 2 +- lib/mapObjects/CBank.cpp | 10 ++-- lib/mapObjects/CGPandoraBox.cpp | 20 ++++---- lib/mapObjects/CGTownInstance.cpp | 10 ++-- lib/mapObjects/CQuest.cpp | 34 ++++++------- lib/mapObjects/CRewardableConstructor.cpp | 6 +-- lib/mapObjects/CRewardableObject.cpp | 18 +++---- lib/mapObjects/MiscObjects.cpp | 22 ++++----- lib/spells/AdventureSpellMechanics.cpp | 2 +- server/CGameHandler.cpp | 26 +++++----- 15 files changed, 148 insertions(+), 86 deletions(-) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 5f94dd1df..efabab3e0 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -632,7 +632,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vectorheroManager->getHeroRole(hero) != HeroRole::MAIN || nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE)) { diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index dcb1ff938..37ab2e637 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1719,7 +1719,7 @@ void CPlayerInterface::acceptTurn() auto playerColor = *cb->getPlayerID(); std::vector components; - components.push_back(Component(Component::FLAG, playerColor.getNum(), 0, 0)); + components.emplace_back(Component::EComponentType::FLAG, playerColor.getNum(), 0, 0); MetaString text; const auto & optDaysWithoutCastle = cb->getPlayerState(playerColor)->daysWithoutCastle; diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index 5a486224b..d9fcea240 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -336,15 +336,61 @@ void CInfoBar::pushComponents(const std::vector & components, std::st prepareComponents(components, message, timer); else { - std::map> reward_map; + std::array, int>, 10> reward_map; for(const auto & c : components) - reward_map[static_cast(c.id)].push_back(c); + { + switch(c.id) + { + case Component::EComponentType::PRIM_SKILL: + case Component::EComponentType::EXPERIENCE: + reward_map.at(0).first.push_back(c); + reward_map.at(0).second = 6; //At most 6, cannot be more + break; + case Component::EComponentType::SEC_SKILL: + reward_map.at(1).first.push_back(c); + reward_map.at(1).second = 8; //At most 8 + break; + case Component::EComponentType::SPELL: + reward_map.at(2).first.push_back(c); + reward_map.at(2).second = 6; //At most 6 + break; + case Component::EComponentType::ARTIFACT: + reward_map.at(3).first.push_back(c); + reward_map.at(3).second = 6; //At most 6 + break; + case Component::EComponentType::CREATURE: + reward_map.at(4).first.push_back(c); + reward_map.at(4).second = 8; //At most 8 + break; + case Component::EComponentType::RESOURCE: + reward_map.at(5).first.push_back(c); + reward_map.at(5).second = 7; //At most 7 + break; + case Component::EComponentType::MORALE: + case Component::EComponentType::LUCK: + reward_map.at(6).first.push_back(c); + reward_map.at(6).second = 2; //At most 2 - 1 for morale + 1 for luck + break; + case Component::EComponentType::BUILDING: + reward_map.at(7).first.push_back(c); + reward_map.at(7).second = 1; //At most 1 - only large icons available AFAIK + break; + case Component::EComponentType::HERO_PORTRAIT: + reward_map.at(8).first.push_back(c); + reward_map.at(8).second = 1; //I do not think than we even can get more than 1 hero + break; + case Component::EComponentType::FLAG: + reward_map.at(9).first.push_back(c); + reward_map.at(9).second = 1; //I do not think than we even can get more than 1 player in notification + break; + default: + logGlobal->warn("Invalid component received!"); + } + } for(const auto & kv : reward_map) - { - auto vector = kv.second; - actualPush(kv.second, message, timer, kv.first == Component::ARTIFACT ? 6 : 8); - } + if(!kv.first.empty()) + actualPush(kv.first, message, timer, kv.second); } popComponents(); } diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 1873e45ec..c3ffc6ceb 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -42,7 +42,7 @@ CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize, EFonts CComponent::CComponent(const Component & c, ESize imageSize, EFonts font) : perDay(false) { - if(c.id == Component::RESOURCE && c.when==-1) + if(c.id == Component::EComponentType::RESOURCE && c.when==-1) perDay = true; init((Etype)c.id, c.subtype, c.val, imageSize, font); diff --git a/lib/NetPacksBase.h b/lib/NetPacksBase.h index 642d63638..e59308baa 100644 --- a/lib/NetPacksBase.h +++ b/lib/NetPacksBase.h @@ -197,8 +197,24 @@ public: struct Component { - enum EComponentType {PRIM_SKILL, SEC_SKILL, RESOURCE, CREATURE, ARTIFACT, EXPERIENCE, SPELL, MORALE, LUCK, BUILDING, HERO_PORTRAIT, FLAG}; - ui16 id = 0, subtype = 0; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels) + enum class EComponentType : uint8_t + { + PRIM_SKILL, + SEC_SKILL, + RESOURCE, + CREATURE, + ARTIFACT, + EXPERIENCE, + SPELL, + MORALE, + LUCK, + BUILDING, + HERO_PORTRAIT, + FLAG, + INVALID //should be last + }; + EComponentType id = EComponentType::INVALID; + ui16 subtype = 0; //id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels si32 val = 0; // + give; - take si16 when = 0; // 0 - now; +x - within x days; -x - per x days diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 279af9bc8..5e0de1263 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -2498,7 +2498,7 @@ void YourTurn::applyGs(CGameState * gs) const } Component::Component(const CStackBasicDescriptor & stack) - : id(CREATURE) + : id(EComponentType::CREATURE) , subtype(stack.type->idNumber) , val(stack.count) { diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 777784bbd..cfdbacd7d 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -190,7 +190,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const break; } cb->giveHeroBonus(&gbonus); - iw.components.emplace_back(Component::MORALE, 0, -1, 0); + iw.components.emplace_back(Component::EComponentType::MORALE, 0, -1, 0); iw.soundID = soundBase::GRAVEYARD; break; } @@ -201,7 +201,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const gb.id = hero->id.getNum(); cb->giveHeroBonus(&gb); textID = 107; - iw.components.emplace_back(Component::LUCK, 0, -2, 0); + iw.components.emplace_back(Component::EComponentType::LUCK, 0, -2, 0); break; } case Obj::CREATURE_BANK: @@ -225,7 +225,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const { if (bc->resources[it] != 0) { - iw.components.emplace_back(Component::RESOURCE, it, bc->resources[it], 0); + iw.components.emplace_back(Component::EComponentType::RESOURCE, it, bc->resources[it], 0); loot << "%d %s"; loot.addReplacement(iw.components.back().val); loot.addReplacement(MetaString::RES_NAMES, iw.components.back().subtype); @@ -235,7 +235,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const //grant artifacts for (auto & elem : bc->artifacts) { - iw.components.emplace_back(Component::ARTIFACT, elem, 0, 0); + iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0); loot << "%s"; loot.addReplacement(MetaString::ART_NAMES, elem); cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE); @@ -279,7 +279,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const if(hero->canLearnSpell(spell)) { spells.insert(spellId); - iw.components.emplace_back(Component::SPELL, spellId, 0, 0); + iw.components.emplace_back(Component::EComponentType::SPELL, spellId, 0, 0); } } else diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index f5f652198..10f8e66f0 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -83,14 +83,14 @@ void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const iw.text.addReplacement(h->getNameTranslated()); if(expVal) - iw.components.emplace_back(Component::EXPERIENCE, 0, static_cast(expVal), 0); + iw.components.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(expVal), 0); for(int i=0; ishowInfoDialog(&iw); @@ -144,7 +144,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const const auto * spell = (*i).toSpell(VLC->spells()); if(h->canLearnSpell(spell)) { - iw.components.emplace_back(Component::SPELL, *i, 0, 0); + iw.components.emplace_back(Component::EComponentType::SPELL, *i, 0, 0); spellsToGive.insert(*i); } if(spellsToGive.size() == 8) //display up to 8 spells at once @@ -172,7 +172,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const if(manaDiff) { getText(iw,hadGuardians,manaDiff,176,177,h); - iw.components.emplace_back(Component::PRIM_SKILL, 5, manaDiff, 0); + iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 5, manaDiff, 0); cb->showInfoDialog(&iw); cb->setManaPoints(h->id, h->mana + manaDiff); } @@ -180,7 +180,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const if(moraleDiff) { getText(iw,hadGuardians,moraleDiff,178,179,h); - iw.components.emplace_back(Component::MORALE, 0, moraleDiff, 0); + iw.components.emplace_back(Component::EComponentType::MORALE, 0, moraleDiff, 0); cb->showInfoDialog(&iw); GiveBonus gb; gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::MORALE,Bonus::OBJECT,moraleDiff,id.getNum(),""); @@ -191,7 +191,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const if(luckDiff) { getText(iw,hadGuardians,luckDiff,180,181,h); - iw.components.emplace_back(Component::LUCK, 0, luckDiff, 0); + iw.components.emplace_back(Component::EComponentType::LUCK, 0, luckDiff, 0); cb->showInfoDialog(&iw); GiveBonus gb; gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,luckDiff,id.getNum(),""); @@ -204,7 +204,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const for(int i=0; i 0) - iw.components.emplace_back(Component::RESOURCE, i, resources[i], 0); + iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0); } if(!iw.components.empty()) { @@ -231,7 +231,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const iw.text.addReplacement(h->getNameTranslated()); for(const auto & elem : artifacts) { - iw.components.emplace_back(Component::ARTIFACT, elem, 0, 0); + iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0); if(iw.components.size() >= 14) { cb->showInfoDialog(&iw); diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 296747a18..b601fcc96 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1735,31 +1735,31 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const case BuildingSubID::KNOWLEDGE_VISITING_BONUS: //wall of knowledge what = PrimarySkill::KNOWLEDGE; val = 1; - iw.components.emplace_back(Component::PRIM_SKILL, 3, 1, 0); + iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 3, 1, 0); break; case BuildingSubID::SPELL_POWER_VISITING_BONUS: //order of fire what = PrimarySkill::SPELL_POWER; val = 1; - iw.components.emplace_back(Component::PRIM_SKILL, 2, 1, 0); + iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 2, 1, 0); break; case BuildingSubID::ATTACK_VISITING_BONUS: //hall of Valhalla what = PrimarySkill::ATTACK; val = 1; - iw.components.emplace_back(Component::PRIM_SKILL, 0, 1, 0); + iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 0, 1, 0); break; case BuildingSubID::EXPERIENCE_VISITING_BONUS: //academy of battle scholars what = PrimarySkill::EXPERIENCE; val = static_cast(h->calculateXp(1000)); - iw.components.emplace_back(Component::EXPERIENCE, 0, val, 0); + iw.components.emplace_back(Component::EComponentType::EXPERIENCE, 0, val, 0); break; case BuildingSubID::DEFENSE_VISITING_BONUS: //cage of warlords what = PrimarySkill::DEFENSE; val = 1; - iw.components.emplace_back(Component::PRIM_SKILL, 1, 1, 0); + iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 1, 1, 0); break; case BuildingSubID::CUSTOM_VISITING_BONUS: diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 9388f7e35..4fafb3a75 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -188,7 +188,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector &components switch (missionType) { case MISSION_LEVEL: - components.emplace_back(Component::EXPERIENCE, 0, m13489val, 0); + components.emplace_back(Component::EComponentType::EXPERIENCE, 0, m13489val, 0); if(!isCustom) iwText.addReplacement(m13489val); break; @@ -199,7 +199,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector &components { if(m2stats[i]) { - components.emplace_back(Component::PRIM_SKILL, i, m2stats[i], 0); + components.emplace_back(Component::EComponentType::PRIM_SKILL, i, m2stats[i], 0); loot << "%d %s"; loot.addReplacement(m2stats[i]); loot.addReplacement(VLC->generaltexth->primarySkillNames[i]); @@ -210,13 +210,13 @@ void CQuest::getVisitText(MetaString &iwText, std::vector &components } break; case MISSION_KILL_HERO: - components.emplace_back(Component::HERO_PORTRAIT, heroPortrait, 0, 0); + components.emplace_back(Component::EComponentType::HERO_PORTRAIT, heroPortrait, 0, 0); if(!isCustom) addReplacements(iwText, text); break; case MISSION_HERO: //FIXME: portrait may not match hero, if custom portrait was set in map editor - components.emplace_back(Component::HERO_PORTRAIT, VLC->heroh->objects[m13489val]->imageIndex, 0, 0); + components.emplace_back(Component::EComponentType::HERO_PORTRAIT, VLC->heroh->objects[m13489val]->imageIndex, 0, 0); if(!isCustom) iwText.addReplacement(VLC->heroh->objects[m13489val]->getNameTranslated()); break; @@ -234,7 +234,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector &components MetaString loot; for(const auto & elem : m5arts) { - components.emplace_back(Component::ARTIFACT, elem, 0, 0); + components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0); loot << "%s"; loot.addReplacement(MetaString::ART_NAMES, elem); } @@ -262,7 +262,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector &components { if(m7resources[i]) { - components.emplace_back(Component::RESOURCE, i, m7resources[i], 0); + components.emplace_back(Component::EComponentType::RESOURCE, i, m7resources[i], 0); loot << "%d %s"; loot.addReplacement(m7resources[i]); loot.addReplacement(MetaString::RES_NAMES, i); @@ -273,7 +273,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector &components } break; case MISSION_PLAYER: - components.emplace_back(Component::FLAG, m13489val, 0, 0); + components.emplace_back(Component::EComponentType::FLAG, m13489val, 0, 0); if(!isCustom) iwText.addReplacement(VLC->generaltexth->colors[m13489val]); break; @@ -646,34 +646,34 @@ void CGSeerHut::getCompletionText(MetaString &text, std::vector &comp switch(rewardType) { case EXPERIENCE: - components.emplace_back(Component::EXPERIENCE, 0, static_cast(h->calculateXp(rVal)), 0); + components.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(h->calculateXp(rVal)), 0); break; case MANA_POINTS: - components.emplace_back(Component::PRIM_SKILL, 5, rVal, 0); + components.emplace_back(Component::EComponentType::PRIM_SKILL, 5, rVal, 0); break; case MORALE_BONUS: - components.emplace_back(Component::MORALE, 0, rVal, 0); + components.emplace_back(Component::EComponentType::MORALE, 0, rVal, 0); break; case LUCK_BONUS: - components.emplace_back(Component::LUCK, 0, rVal, 0); + components.emplace_back(Component::EComponentType::LUCK, 0, rVal, 0); break; case RESOURCES: - components.emplace_back(Component::RESOURCE, rID, rVal, 0); + components.emplace_back(Component::EComponentType::RESOURCE, rID, rVal, 0); break; case PRIMARY_SKILL: - components.emplace_back(Component::PRIM_SKILL, rID, rVal, 0); + components.emplace_back(Component::EComponentType::PRIM_SKILL, rID, rVal, 0); break; case SECONDARY_SKILL: - components.emplace_back(Component::SEC_SKILL, rID, rVal, 0); + components.emplace_back(Component::EComponentType::SEC_SKILL, rID, rVal, 0); break; case ARTIFACT: - components.emplace_back(Component::ARTIFACT, rID, 0, 0); + components.emplace_back(Component::EComponentType::ARTIFACT, rID, 0, 0); break; case SPELL: - components.emplace_back(Component::SPELL, rID, 0, 0); + components.emplace_back(Component::EComponentType::SPELL, rID, 0, 0); break; case CREATURE: - components.emplace_back(Component::CREATURE, rID, rVal, 0); + components.emplace_back(Component::EComponentType::CREATURE, rID, rVal, 0); break; } } diff --git a/lib/mapObjects/CRewardableConstructor.cpp b/lib/mapObjects/CRewardableConstructor.cpp index b5129a205..1ed10ed31 100644 --- a/lib/mapObjects/CRewardableConstructor.cpp +++ b/lib/mapObjects/CRewardableConstructor.cpp @@ -116,9 +116,9 @@ void CRandomRewardObjectInfo::configureReward(CRewardableObject * object, CRando bonus.sid = object->ID; //TODO: bonus.description = object->getObjectName(); if (bonus.type == Bonus::MORALE) - reward.extraComponents.emplace_back(Component::MORALE, 0, bonus.val, 0); + reward.extraComponents.emplace_back(Component::EComponentType::MORALE, 0, bonus.val, 0); if (bonus.type == Bonus::LUCK) - reward.extraComponents.emplace_back(Component::LUCK, 0, bonus.val, 0); + reward.extraComponents.emplace_back(Component::EComponentType::LUCK, 0, bonus.val, 0); } reward.primary = JsonRandom::loadPrimary(source["primary"], rng); @@ -137,7 +137,7 @@ void CRandomRewardObjectInfo::configureReward(CRewardableObject * object, CRando CreatureID from (VLC->modh->identifiers.getIdentifier (node.second.meta, "creature", node.first) .get()); CreatureID dest (VLC->modh->identifiers.getIdentifier (node.second.meta, "creature", node.second.String()).get()); - reward.extraComponents.emplace_back(Component::CREATURE, dest.getNum(), 0, 0); + reward.extraComponents.emplace_back(Component::EComponentType::CREATURE, dest.getNum(), 0, 0); reward.creaturesChange[from] = dest; } diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index bb92bad65..c3eb189f5 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -432,36 +432,36 @@ void CRewardInfo::loadComponents(std::vector & comps, if (heroExperience) { - comps.emplace_back(Component::EXPERIENCE, 0, static_cast(h->calculateXp(heroExperience)), 0); + comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(h->calculateXp(heroExperience)), 0); } if (heroLevel) - comps.emplace_back(Component::EXPERIENCE, 1, heroLevel, 0); + comps.emplace_back(Component::EComponentType::EXPERIENCE, 1, heroLevel, 0); if (manaDiff || manaPercentage >= 0) - comps.emplace_back(Component::PRIM_SKILL, 5, calculateManaPoints(h) - h->mana, 0); + comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, calculateManaPoints(h) - h->mana, 0); for (size_t i=0; i(i), primary[i], 0); + comps.emplace_back(Component::EComponentType::PRIM_SKILL, static_cast(i), primary[i], 0); } for(const auto & entry : secondary) - comps.emplace_back(Component::SEC_SKILL, entry.first, entry.second, 0); + comps.emplace_back(Component::EComponentType::SEC_SKILL, entry.first, entry.second, 0); for(const auto & entry : artifacts) - comps.emplace_back(Component::ARTIFACT, entry, 1, 0); + comps.emplace_back(Component::EComponentType::ARTIFACT, entry, 1, 0); for(const auto & entry : spells) - comps.emplace_back(Component::SPELL, entry, 1, 0); + comps.emplace_back(Component::EComponentType::SPELL, entry, 1, 0); for(const auto & entry : creatures) - comps.emplace_back(Component::CREATURE, entry.type->idNumber, entry.count, 0); + comps.emplace_back(Component::EComponentType::CREATURE, entry.type->idNumber, entry.count, 0); for (size_t i=0; i(i), resources[i], 0); + comps.emplace_back(Component::EComponentType::RESOURCE, static_cast(i), resources[i], 0); } } diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 7380af760..59a2a7ab3 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -562,14 +562,14 @@ void CGCreature::giveReward(const CGHeroInstance * h) const for(int i = 0; i < resources.size(); i++) { if(resources[i] > 0) - iw.components.emplace_back(Component::RESOURCE, i, resources[i], 0); + iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0); } } if(gainedArtifact != ArtifactID::NONE) { cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE); - iw.components.emplace_back(Component::ARTIFACT, gainedArtifact, 0, 0); + iw.components.emplace_back(Component::EComponentType::ARTIFACT, gainedArtifact, 0, 0); } if(!iw.components.empty()) @@ -720,7 +720,7 @@ void CGMine::flagMine(const PlayerColor & player) const iw.soundID = soundBase::FLAGMINE; iw.text.addTxt(MetaString::MINE_EVNTS,producedResource); //not use subID, abandoned mines uses default mine texts iw.player = player; - iw.components.emplace_back(Component::RESOURCE, producedResource, producedQuantity, -1); + iw.components.emplace_back(Component::EComponentType::RESOURCE, producedResource, producedQuantity, -1); cb->showInfoDialog(&iw); } @@ -877,7 +877,7 @@ void CGResource::collectRes(const PlayerColor & player) const sii.text.addTxt(MetaString::ADVOB_TXT,113); sii.text.addReplacement(MetaString::RES_NAMES, subID); } - sii.components.emplace_back(Component::RESOURCE,subID,amount,0); + sii.components.emplace_back(Component::EComponentType::RESOURCE,subID,amount,0); sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6); cb->showInfoDialog(&sii); cb->removeObject(this); @@ -1303,7 +1303,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const { case Obj::ARTIFACT: { - iw.components.emplace_back(Component::ARTIFACT, subID, 0, 0); + iw.components.emplace_back(Component::EComponentType::ARTIFACT, subID, 0, 0); if(message.length()) iw.text << message; else @@ -1313,7 +1313,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const case Obj::SPELL_SCROLL: { int spellID = storedArtifact->getGivenSpellID(); - iw.components.emplace_back(Component::SPELL, spellID, 0, 0); + iw.components.emplace_back(Component::EComponentType::SPELL, spellID, 0, 0); if(message.length()) iw.text << message; else @@ -1441,7 +1441,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const } else //give sec skill { - iw.components.emplace_back(Component::SEC_SKILL, ability, 1, 0); + iw.components.emplace_back(Component::EComponentType::SEC_SKILL, ability, 1, 0); txt_id = 171; cb->changeSecSkill(h, SecondarySkill(ability), 1, true); } @@ -1568,7 +1568,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const spells.insert(spell); cb->changeSpells(h, true, spells); - iw.components.emplace_back(Component::SPELL, spell, 0, 0); + iw.components.emplace_back(Component::EComponentType::SPELL, spell, 0, 0); } cb->showInfoDialog(&iw); @@ -1670,18 +1670,18 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const { case PRIM_SKILL: cb->changePrimSkill(h,static_cast(bid),+1); - iw.components.emplace_back(Component::PRIM_SKILL, bid, +1, 0); + iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, bid, +1, 0); break; case SECONDARY_SKILL: cb->changeSecSkill(h,SecondarySkill(bid),+1); - iw.components.emplace_back(Component::SEC_SKILL, bid, ssl + 1, 0); + iw.components.emplace_back(Component::EComponentType::SEC_SKILL, bid, ssl + 1, 0); break; case SPELL: { std::set hlp; hlp.insert(SpellID(bid)); cb->changeSpells(h,true,hlp); - iw.components.emplace_back(Component::SPELL, bid, 0, 0); + iw.components.emplace_back(Component::EComponentType::SPELL, bid, 0, 0); } break; default: diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 5bc687075..a00b26502 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -505,7 +505,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons request.player = parameters.caster->getOwner(); request.title.addTxt(MetaString::JK_TXT, 40); request.description.addTxt(MetaString::JK_TXT, 41); - request.icon.id = Component::SPELL; + request.icon.id = Component::EComponentType::SPELL; request.icon.subtype = owner->id.toEnum(); env->genericQuery(&request, request.player, queryCallback); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 59d6c5edf..acfc3a511 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -837,9 +837,9 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con for (auto art : arts) //TODO; separate function to display loot for various ojects? { - iw.components.push_back(Component( - Component::ARTIFACT, art->artType->getId(), - art->artType->getId() == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0)); + iw.components.emplace_back( + Component::EComponentType::ARTIFACT, art->artType->getId(), + art->artType->getId() == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0); if (iw.components.size() >= 14) { sendAndApply(&iw); @@ -881,7 +881,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con iw.text.addReplacement(MetaString::SPELL_NAME, it->toEnum()); if (i == cs.spells.size() - 2) //we just added pre-last name iw.text.addReplacement(MetaString::GENERAL_TXT, 141); // " and " - iw.components.push_back(Component(Component::SPELL, *it, 0, 0)); + iw.components.emplace_back(Component::EComponentType::SPELL, *it, 0, 0); } sendAndApply(&iw); sendAndApply(&cs); @@ -2818,7 +2818,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t h2->getSecSkillLevel(SecondarySkill::SCHOLAR)); InfoWindow iw; iw.player = h1->tempOwner; - iw.components.push_back(Component(Component::SEC_SKILL, 18, ScholarSkillLevel, 0)); + iw.components.emplace_back(Component::EComponentType::SEC_SKILL, 18, ScholarSkillLevel, 0); iw.text.addTxt(MetaString::GENERAL_TXT, 139);//"%s, who has studied magic extensively, iw.text.addReplacement(h1->getNameTranslated()); @@ -2829,7 +2829,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t int size = static_cast(cs2.spells.size()); for (auto it : cs2.spells) { - iw.components.push_back(Component(Component::SPELL, it, 1, 0)); + iw.components.emplace_back(Component::EComponentType::SPELL, it, 1, 0); iw.text.addTxt(MetaString::SPELL_NAME, it.toEnum()); switch (size--) { @@ -2854,7 +2854,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t int size = static_cast(cs1.spells.size()); for (auto it : cs1.spells) { - iw.components.push_back(Component(Component::SPELL, it, 1, 0)); + iw.components.emplace_back(Component::EComponentType::SPELL, it, 1, 0); iw.text.addTxt(MetaString::SPELL_NAME, it.toEnum()); switch (size--) { @@ -5578,7 +5578,7 @@ void CGameHandler::handleTimeEvents() for (int i=0; iresources.at(i) != n.res.at(player).at(i)) //if resource had changed, we add it to the dialog - iw.components.push_back(Component(Component::RESOURCE,i,n.res.at(player).at(i)-was.at(i),0)); + iw.components.emplace_back(Component::EComponentType::RESOURCE,i,n.res.at(player).at(i)-was.at(i),0); } @@ -5645,7 +5645,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n) if (!town->hasBuilt(i)) { buildStructure(town->id, i, true); - iw.components.push_back(Component(Component::BUILDING, town->subID, i, 0)); + iw.components.emplace_back(Component::EComponentType::BUILDING, town->subID, i, 0); } } @@ -5661,8 +5661,8 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n) if (!town->creatures.at(i).second.empty() && ev.creatures.at(i) > 0)//there is dwelling { sac.creatures[i].first += ev.creatures.at(i); - iw.components.push_back(Component(Component::CREATURE, - town->creatures.at(i).second.back(), ev.creatures.at(i), 0)); + iw.components.emplace_back(Component::EComponentType::CREATURE, + town->creatures.at(i).second.back(), ev.creatures.at(i), 0); } } sendAndApply(&iw); //show dialog @@ -6024,7 +6024,7 @@ void CGameHandler::getVictoryLossMessage(PlayerColor player, const EVictoryLossC if (victoryLossCheckResult.messageToSelf.find("%s") != std::string::npos) out.text.addReplacement(MetaString::COLOR, player.getNum()); - out.components.push_back(Component(Component::FLAG, player.getNum(), 0, 0)); + out.components.emplace_back(Component::EComponentType::FLAG, player.getNum(), 0, 0); } bool CGameHandler::dig(const CGHeroInstance *h) From de211e9f1373a695e716185424a07bc08c9d0856 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 18:54:37 +0300 Subject: [PATCH 19/25] vcmi: all notifications now shows inside the infobar properly No more dangling images or text. --- client/adventureMap/CInfoBar.cpp | 33 ++++++++++++++++++-------------- client/adventureMap/CInfoBar.h | 2 +- client/widgets/CComponent.cpp | 18 +++++++++-------- client/widgets/CComponent.h | 6 ++++-- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index d9fcea240..e0c76e68c 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -173,6 +173,7 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector 4) - size = CComponent::tiny; + } + + if(compsToDisplay.size() > 4) { + maxComponents = 3; + size = CComponent::small; } if(compsToDisplay.size() > 6) - size = CComponent::small; + maxComponents = 4; std::vector> vect; for(const auto & c : compsToDisplay) vect.emplace_back(std::make_shared(c, size, font)); - comps = std::make_shared(vect, imageRect, 4, 4, 1); + comps = std::make_shared(vect, imageRect, 4, 4, 1, maxComponents); } else font = tiny ? FONT_TINY : font; @@ -216,12 +220,10 @@ int CInfoBar::getEstimatedComponentHeight(int numComps) const { if (numComps > 8) //Bigger than 8 components - return invalid value return std::numeric_limits::max(); - else if (numComps > 4) - return 48 + 20; // 24px * 2 rows + 20 to offset else if (numComps > 2) - return 96 + 20; // 32px * 1 row + 20 to offset + return 160; // 32px * 1 row + 20 to offset else if (numComps) - return 128; // 128 px to offset + return 118; // 118 px to offset return 0; } @@ -344,23 +346,23 @@ void CInfoBar::pushComponents(const std::vector & components, std::st case Component::EComponentType::PRIM_SKILL: case Component::EComponentType::EXPERIENCE: reward_map.at(0).first.push_back(c); - reward_map.at(0).second = 6; //At most 6, cannot be more + reward_map.at(0).second = 8; //At most 8, cannot be more break; case Component::EComponentType::SEC_SKILL: reward_map.at(1).first.push_back(c); - reward_map.at(1).second = 8; //At most 8 + reward_map.at(1).second = 4; //At most 4 break; case Component::EComponentType::SPELL: reward_map.at(2).first.push_back(c); - reward_map.at(2).second = 6; //At most 6 + reward_map.at(2).second = 4; //At most 4 break; case Component::EComponentType::ARTIFACT: reward_map.at(3).first.push_back(c); - reward_map.at(3).second = 6; //At most 6 + reward_map.at(3).second = 4; //At most 4, too long names break; case Component::EComponentType::CREATURE: reward_map.at(4).first.push_back(c); - reward_map.at(4).second = 8; //At most 8 + reward_map.at(4).second = 4; //At most 4, too long names break; case Component::EComponentType::RESOURCE: reward_map.at(5).first.push_back(c); @@ -402,6 +404,7 @@ void CInfoBar::prepareComponents(const std::vector & components, std: auto tinyH = CMessage::guessHeight(message,CInfoBar::data_width - 2 * CInfoBar::offset, FONT_TINY); auto header = CMessage::guessHeader(message); auto headerH = CMessage::guessHeight(header, CInfoBar::data_width - 2 * CInfoBar::offset, FONT_SMALL); + auto headerTinyH = CMessage::guessHeight(header, CInfoBar::data_width - 2 * CInfoBar::offset, FONT_TINY); // Order matters - priority form should be chosen first if(imageH + textH < CInfoBar::data_height) @@ -410,6 +413,8 @@ void CInfoBar::prepareComponents(const std::vector & components, std: pushComponents(components, message, tinyH, true, timer); else if(imageH + headerH < CInfoBar::data_height) pushComponents(components, header, headerH, false, timer); + else if(imageH + headerTinyH < CInfoBar::data_height - 2 * CInfoBar::offset) + pushComponents(components, header, headerTinyH, true, timer); else pushComponents(components, "", 0, false, timer); diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 4b7e31497..87a3f3faa 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -56,7 +56,7 @@ private: }; static constexpr int data_width = width - 2 * CVisibleInfo::offset_x; - static constexpr int data_height = height - 2 * CVisibleInfo::offset_y + 4; //yes, +4 is required + static constexpr int data_height = height - 2 * CVisibleInfo::offset_y; class EmptyVisibleInfo : public CVisibleInfo { diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index c3ffc6ceb..2d0a2b035 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -75,7 +75,7 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts auto max = 80; if (size < large) - max = 60; + max = 72; if (size < medium) max = 40; if (size < small) @@ -387,7 +387,7 @@ void CComponentBox::placeComponents(bool selectable) //start next row if ((pos.w != 0 && rows.back().width + comp->pos.w + distance > pos.w) // row is full - || rows.back().comps >= 4) // no more than 4 comps per row + || rows.back().comps >= componentsInRow) { prevComp = nullptr; rows.push_back (RowData (0,0,0)); @@ -453,15 +453,16 @@ void CComponentBox::placeComponents(bool selectable) } CComponentBox::CComponentBox(std::vector> _components, Rect position): - CComponentBox(_components, position, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows) + CComponentBox(_components, position, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows, defaultComponentsInRow) { } -CComponentBox::CComponentBox(std::vector> _components, Rect position, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows): +CComponentBox::CComponentBox(std::vector> _components, Rect position, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows, int componentsInRow): components(_components), betweenImagesMin(betweenImagesMin), betweenSubtitlesMin(betweenSubtitlesMin), - betweenRows(betweenRows) + betweenRows(betweenRows), + componentsInRow(componentsInRow) { type |= REDRAW_PARENT; pos = position + pos.topLeft(); @@ -469,16 +470,17 @@ CComponentBox::CComponentBox(std::vector> _component } CComponentBox::CComponentBox(std::vector> _components, Rect position, std::function _onSelect): - CComponentBox(_components, position, _onSelect, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows) + CComponentBox(_components, position, _onSelect, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows, defaultComponentsInRow) { } -CComponentBox::CComponentBox(std::vector> _components, Rect position, std::function _onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows): +CComponentBox::CComponentBox(std::vector> _components, Rect position, std::function _onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows, int componentsInRow): components(_components.begin(), _components.end()), onSelect(_onSelect), betweenImagesMin(betweenImagesMin), betweenSubtitlesMin(betweenSubtitlesMin), - betweenRows(betweenRows) + betweenRows(betweenRows), + componentsInRow(componentsInRow) { type |= REDRAW_PARENT; pos = position + pos.topLeft(); diff --git a/client/widgets/CComponent.h b/client/widgets/CComponent.h index 99494e805..ecdbdb361 100644 --- a/client/widgets/CComponent.h +++ b/client/widgets/CComponent.h @@ -98,10 +98,12 @@ class CComponentBox : public CIntObject static constexpr int defaultBetweenImagesMin = 20; static constexpr int defaultBetweenSubtitlesMin = 10; static constexpr int defaultBetweenRows = 22; + static constexpr int defaultComponentsInRow = 4; int betweenImagesMin; int betweenSubtitlesMin; int betweenRows; + int componentsInRow; void selectionChanged(std::shared_ptr newSelection); @@ -119,11 +121,11 @@ public: /// constructors for non-selectable components CComponentBox(std::vector> components, Rect position); - CComponentBox(std::vector> components, Rect position, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows); + CComponentBox(std::vector> components, Rect position, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows, int componentsInRow); /// constructor for selectable components /// will also create "or" labels between components /// onSelect - optional function that will be called every time on selection change CComponentBox(std::vector> components, Rect position, std::function onSelect = nullptr); - CComponentBox(std::vector> components, Rect position, std::function onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows); + CComponentBox(std::vector> components, Rect position, std::function onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows, int componentsInRow); }; From bf677848406831879c9b1d29422d2628e4ea8089 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 19:00:46 +0300 Subject: [PATCH 20/25] vcmi: correctly show spells from SpellInt.def --- client/widgets/CComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 2d0a2b035..3cd07d8d1 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -144,7 +144,7 @@ size_t CComponent::getIndex() case creature: return CGI->creatures()->getByIndex(subtype)->getIconIndex(); case artifact: return CGI->artifacts()->getByIndex(subtype)->getIconIndex(); case experience: return 4; - case spell: return subtype; + case spell: return (size < large) ? subtype + 1 : subtype; case morale: return val+3; case luck: return val+3; case building: return val; From 3d33da0a9ebc576e0bf547f5b812f2d05a1d180e Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 10 Mar 2023 21:29:20 +0300 Subject: [PATCH 21/25] vcmi: request pop all when hero is moved This way new pickup will show immidately. --- client/CPlayerInterface.cpp | 1 + client/adventureMap/CInfoBar.cpp | 17 +++++++++++++---- client/adventureMap/CInfoBar.h | 6 +++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 37ab2e637..40f508deb 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -324,6 +324,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose) if (!hero) return; + adventureInt->infoBar->requestPopAll(); if (details.result == TryMoveHero::EMBARK || details.result == TryMoveHero::DISEMBARK) { if (hero->getRemovalSound()) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index e0c76e68c..f47d950e3 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -268,7 +268,7 @@ void CInfoBar::tick() { removeUsedEvents(TIME); if(GH.topInt() == adventureInt) - popComponents(); + popComponents(true); } void CInfoBar::clickLeft(tribool down, bool previousState) @@ -280,7 +280,7 @@ void CInfoBar::clickLeft(tribool down, bool previousState) else if(state == GAME) showDate(); else - popComponents(); + popComponents(true); } } @@ -334,6 +334,8 @@ void CInfoBar::pushComponents(const std::vector & components, std::st vect.erase(vect.begin(), vect.begin() + std::min(vect.size(), max)); }; }; + if(shouldPopAll) + popAll(); if(components.empty()) prepareComponents(components, message, timer); else @@ -421,20 +423,27 @@ void CInfoBar::prepareComponents(const std::vector & components, std: return; } +void CInfoBar::requestPopAll() +{ + shouldPopAll = true; +} + void CInfoBar::popAll() { componentsQueue = {}; + shouldPopAll = false; } -void CInfoBar::popComponents() +void CInfoBar::popComponents(bool remove) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); + if(remove && !componentsQueue.empty()) + componentsQueue.pop(); if(!componentsQueue.empty()) { state = COMPONENT; const auto & extracted = componentsQueue.front(); visibleInfo = std::make_shared(extracted.first); - componentsQueue.pop(); setTimer(extracted.second); redraw(); return; diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 87a3f3faa..fa6e4cd6e 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -138,6 +138,7 @@ private: std::shared_ptr visibleInfo; EState state; + bool shouldPopAll = false; std::queue> componentsQueue; @@ -145,7 +146,7 @@ private: void showComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer); void pushComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer); void prepareComponents(const std::vector & comps, std::string message, int timer); - void popComponents(); + void popComponents(bool remove = false); //removes all information about current state, deactivates timer (if any) void reset(); @@ -170,6 +171,9 @@ public: /// Remove all queued components void popAll(); + /// Request infobar to pop all after next InfoWindow arrives. + void requestPopAll(); + /// print enemy turn progress void startEnemyTurn(PlayerColor color); From 4c31db52be4f0583aa31be9c200d113464fc1e51 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Sat, 11 Mar 2023 00:52:38 +0300 Subject: [PATCH 22/25] vcmi: add artifact component to second grail msg --- server/CGameHandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index acfc3a511..912de1c20 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -6057,6 +6057,7 @@ bool CGameHandler::dig(const CGHeroInstance *h) sendAndApply(&iw); iw.soundID = soundBase::invalid; + iw.components.emplace_back(Component::EComponentType::ARTIFACT, ArtifactID::GRAIL, 0, 0); iw.text.clear(); iw.text.addTxt(MetaString::ART_DESCR, ArtifactID::GRAIL); sendAndApply(&iw); From fafe352f932ff0c7d57fd4583d794789af9921e2 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Sat, 11 Mar 2023 21:18:01 +0300 Subject: [PATCH 23/25] vcmi: bump max serialization version And also - now we can choose between 3 states for rewardables actually. --- lib/mapObjects/CRewardableConstructor.cpp | 7 +++++-- lib/mapObjects/CRewardableObject.cpp | 7 +++---- lib/mapObjects/CRewardableObject.h | 5 +++-- lib/serializer/CSerializer.h | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/mapObjects/CRewardableConstructor.cpp b/lib/mapObjects/CRewardableConstructor.cpp index 1ed10ed31..7e860c074 100644 --- a/lib/mapObjects/CRewardableConstructor.cpp +++ b/lib/mapObjects/CRewardableConstructor.cpp @@ -230,8 +230,11 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando configureResetInfo(object, rng, object->resetParameters, parameters["resetParameters"]); object->canRefuse = parameters["canRefuse"].Bool(); - - object->showInInfobox = parameters["showInInfobox"].Bool(); + + if(parameters["showInInfobox"].isNull()) + object->infoWindowType = EInfoWindowMode::AUTO; + else + object->infoWindowType = parameters["showInInfobox"].Bool() ? EInfoWindowMode::INFO : EInfoWindowMode::MODAL; auto visitMode = parameters["visitMode"].String(); for(int i = 0; i < Rewardable::VisitModeString.size(); ++i) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index c3eb189f5..1d3138582 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -134,15 +134,14 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const { auto vi = info[index]; logGlobal->debug("Granting reward %d. Message says: %s", index, vi.message.toString()); - if (showInInfobox || !vi.message.toString().empty()) // show message only if it is not empty or in infobox + // show message only if it is not empty or in infobox + if (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 = EInfoWindowMode::AUTO; - if(showInInfobox) - iw.type = EInfoWindowMode::INFO; + iw.type = infoWindowType; cb->showInfoDialog(&iw); } // grant reward afterwards. Note that it may remove object diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 03004da74..c120abfa9 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -307,7 +307,7 @@ protected: bool canRefuse; /// if true - object info will shown in infobox (like resource pickup) - bool showInInfobox; + EInfoWindowMode infoWindowType = EInfoWindowMode::AUTO; /// return true if this object was "cleared" before and no longer has rewards applicable to selected hero /// unlike wasVisited, this method uses information not available to player owner, for example, if object was cleared by another player before @@ -348,13 +348,14 @@ public: h & static_cast(*this); h & info; h & canRefuse; - h & showInInfobox; h & resetParameters; h & onSelect; h & visitMode; h & selectMode; h & selectedReward; h & onceVisitableObjectCleared; + if (version >= 817) + h & infoWindowType; } // for configuration/object setup diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 9b12c3c97..e595fadd5 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN -const ui32 SERIALIZATION_VERSION = 816; +const ui32 SERIALIZATION_VERSION = 817; const ui32 MINIMAL_SERIALIZATION_VERSION = 813; const std::string SAVEGAME_MAGIC = "VCMISVG"; From 45dcd95f2055d186b0ee76fa8c18df19b79690ef Mon Sep 17 00:00:00 2001 From: Konstantin Date: Sat, 11 Mar 2023 21:21:34 +0300 Subject: [PATCH 24/25] miscObjects: fix whirpool display It should display negative numbers, not positive --- 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 59a2a7ab3..783f3e696 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1217,7 +1217,7 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; iw.text.addTxt(MetaString::ADVOB_TXT, 168); - iw.components.emplace_back(CStackBasicDescriptor(h->getCreature(targetstack), countToTake)); + iw.components.emplace_back(CStackBasicDescriptor(h->getCreature(targetstack), -countToTake)); cb->showInfoDialog(&iw); cb->changeStackCount(StackLocation(h, targetstack), -countToTake); } From 4df4a872c09e3a417b6d7fcea672121bf47aa199 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Sun, 12 Mar 2023 14:59:44 +0300 Subject: [PATCH 25/25] showInfoDialog: fix waitWhileDialog regression --- client/CPlayerInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 40f508deb..6e8ebdd7e 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1041,6 +1041,7 @@ void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &t if(autoTryHover || type == EInfoWindowMode::INFO) { + waitWhileDialog(); //Fix for mantis #98 adventureInt->infoBar->pushComponents(components, text, timer); if (makingTurn && GH.listInt.size() && LOCPLINT == this)