1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge pull request #1645 from rilian-la-te/infobox-pickups

Allow rewardables to show its rewards in info bar
This commit is contained in:
Ivan Savenko 2023-03-12 15:33:15 +02:00 committed by GitHub
commit e764a7255d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 724 additions and 374 deletions

View File

@ -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<Component> & components, int soundID)
void AIGateway::showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID)
{
LOG_TRACE_PARAMS(logAi, "soundID '%i'", soundID);
NET_EVENT_HANDLER;
@ -632,7 +632,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
// TODO: Find better way to understand it is Chest of Treasures
if(hero.validAndSet()
&& components.size() == 2
&& components.front().id == Component::RESOURCE
&& components.front().id == Component::EComponentType::RESOURCE
&& (nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN
|| nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE))
{

View File

@ -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<Component> & components, int soundID) override;
void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
void requestRealized(PackageApplied * pa) override;
void receivedResource() override;
void objectRemoved(const CGObjectInstance * obj) override;

View File

@ -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<Component> & components, int soundID)
void VCAI::showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID)
{
LOG_TRACE_PARAMS(logAi, "soundID '%i'", soundID);
NET_EVENT_HANDLER;

View File

@ -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<Component> & components, int soundID) override;
void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
void requestRealized(PackageApplied * pa) override;
void receivedResource() override;
void objectRemoved(const CGObjectInstance * obj) override;

View File

@ -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",

View File

@ -25,13 +25,69 @@
"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.infoBarPick.hover" : "Сообщения в информационной панели",
"vcmi.adventureOptions.infoBarPick.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 +122,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",

View File

@ -32,7 +32,6 @@
"author" : "Команда VCMI",
"modType" : "Графический",
"skipValidation" : true,
"translations" : [
"config/vcmi/russian.json"
]

View File

@ -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,

View File

@ -78,7 +78,6 @@ public:
void ambientStopAllChannels();
// Sets
std::vector<soundBase::soundID> pickupSounds;
std::vector<soundBase::soundID> battleIntroSounds;
};

View File

@ -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())
@ -1031,18 +1032,23 @@ 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<Component> & components, int soundID)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
waitWhileDialog(); //Fix for mantis #98
CCS->soundh->playSoundFromSet(CCS->soundh->pickupSounds);
adventureInt->infoBar->showComponent(comp, message);
}
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)
{
waitWhileDialog(); //Fix for mantis #98
adventureInt->infoBar->pushComponents(components, text, timer);
if (makingTurn && GH.listInt.size() && LOCPLINT == this)
CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
return;
}
void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<Component> & components, int soundID)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if (settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())
{
return;
@ -1093,7 +1099,7 @@ void CPlayerInterface::showInfoDialogAndWait(std::vector<Component> & components
std::string str;
text.toString(str);
showInfoDialog(str, components, 0);
showInfoDialog(EInfoWindowMode::MODAL, str, components, 0);
waitWhileDialog();
}
@ -1715,7 +1721,7 @@ void CPlayerInterface::acceptTurn()
auto playerColor = *cb->getPlayerID();
std::vector<Component> 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;
@ -1773,6 +1779,16 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance * h)
void CPlayerInterface::updateInfo(const CGObjectInstance * specific)
{
bool isHero = dynamic_cast<const CGHeroInstance *>(specific) != nullptr;
bool changedHero = dynamic_cast<const CGHeroInstance *>(specific) != adventureInt->curHero();
bool isTown = dynamic_cast<const CGTownInstance *>(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 +1899,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<CArtifactHolder*>(isa.get());
@ -1904,7 +1922,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 +1951,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<CArtifactHolder*>(isa.get());
@ -1944,7 +1964,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<CArtifactHolder*>(isa.get());

View File

@ -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<Component> & components, int soundID) override;
void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & 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<Component> &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<ObjectPosInfo> & objectPositions, bool showTerrain) override;

View File

@ -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 {};

View File

@ -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;

View File

@ -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();
@ -880,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<const CGDwelling*>(cl.getObj(ObjectInstanceID(pack.id1)));
const CArmedInstance *dst = dynamic_cast<const CArmedInstance*>(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)));
@ -909,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));
@ -918,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));
@ -926,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);

View File

@ -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))),
@ -869,6 +869,8 @@ boost::optional<Point> 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)

View File

@ -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,16 +164,67 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo()
}
}
CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const Component & compToDisplay, std::string message)
CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector<Component> & compsToDisplay, std::string message, int textH, bool tiny)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>("ADSTATOT", 1, 0);
auto fullRect = Rect(0, 0, data_width, data_height);
auto textRect = fullRect;
auto imageRect = fullRect;
auto font = FONT_SMALL;
auto maxComponents = 2;
comp = std::make_shared<CComponent>(compToDisplay);
comp->moveTo(Point(pos.x+47, pos.y+50));
if(!compsToDisplay.empty())
{
auto size = CComponent::large;
if(compsToDisplay.size() > 2)
{
size = CComponent::medium;
font = FONT_TINY;
}
if(!message.empty())
{
textRect = Rect(CInfoBar::offset,
CInfoBar::offset,
data_width - 2 * CInfoBar::offset,
textH);
imageRect = Rect(CInfoBar::offset,
textH,
data_width - 2 * CInfoBar::offset,
CInfoBar::data_height - 2* CInfoBar::offset - textH);
}
if(compsToDisplay.size() > 4) {
maxComponents = 3;
size = CComponent::small;
}
if(compsToDisplay.size() > 6)
maxComponents = 4;
text = std::make_shared<CTextBox>(message, Rect(10, 4, 160, 50), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
std::vector<std::shared_ptr<CComponent>> vect;
for(const auto & c : compsToDisplay)
vect.emplace_back(std::make_shared<CComponent>(c, size, font));
comps = std::make_shared<CComponentBox>(vect, imageRect, 4, 4, 1, maxComponents);
}
else
font = tiny ? FONT_TINY : font;
if(!message.empty())
text = std::make_shared<CTextBox>(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<int>::max();
else if (numComps > 2)
return 160; // 32px * 1 row + 20 to offset
else if (numComps)
return 118; // 118 px to offset
return 0;
}
void CInfoBar::playNewDaySound()
@ -216,7 +268,7 @@ void CInfoBar::tick()
{
removeUsedEvents(TIME);
if(GH.topInt() == adventureInt)
showSelection();
popComponents(true);
}
void CInfoBar::clickLeft(tribool down, bool previousState)
@ -228,7 +280,7 @@ void CInfoBar::clickLeft(tribool down, bool previousState)
else if(state == GAME)
showDate();
else
showSelection();
popComponents(true);
}
}
@ -256,6 +308,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);
@ -266,13 +322,144 @@ void CInfoBar::showDate()
redraw();
}
void CInfoBar::showComponent(const Component & comp, std::string message)
void CInfoBar::pushComponents(const std::vector<Component> & components, std::string message, int timer)
{
auto actualPush = [&](const std::vector<Component> & components, std::string message, int timer, size_t max){
std::vector<Component> vect = components; //I do not know currently how to avoid copy here
while(!vect.empty())
{
std::vector<Component> 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(shouldPopAll)
popAll();
if(components.empty())
prepareComponents(components, message, timer);
else
{
std::array<std::pair<std::vector<Component>, int>, 10> reward_map;
for(const auto & c : components)
{
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 = 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 = 4; //At most 4
break;
case Component::EComponentType::SPELL:
reward_map.at(2).first.push_back(c);
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 = 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 = 4; //At most 4, too long names
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)
if(!kv.first.empty())
actualPush(kv.first, message, timer, kv.second);
}
popComponents();
}
void CInfoBar::prepareComponents(const std::vector<Component> & 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);
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)
pushComponents(components, message, textH, false, timer);
else if(!imageH && tinyH < CInfoBar::data_height)
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);
return;
}
void CInfoBar::requestPopAll()
{
shouldPopAll = true;
}
void CInfoBar::popAll()
{
componentsQueue = {};
shouldPopAll = false;
}
void CInfoBar::popComponents(bool remove)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
state = COMPONENT;
visibleInfo = std::make_shared<VisibleComponentInfo>(comp, message);
setTimer(3000);
redraw();
if(remove && !componentsQueue.empty())
componentsQueue.pop();
if(!componentsQueue.empty())
{
state = COMPONENT;
const auto & extracted = componentsQueue.front();
visibleInfo = std::make_shared<VisibleComponentInfo>(extracted.first);
setTimer(extracted.second);
redraw();
return;
}
showSelection();
}
void CInfoBar::pushComponents(const std::vector<Component> & comps, std::string message, int textH, bool tiny, int timer)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
componentsQueue.emplace(VisibleComponentInfo::Cache(comps, message, textH, tiny), timer);
}
bool CInfoBar::showingComponents()
{
return state == COMPONENT;
}
void CInfoBar::startEnemyTurn(PlayerColor color)

View File

@ -23,6 +23,7 @@ VCMI_LIB_NAMESPACE_END
class CAnimImage;
class CShowableAnim;
class CComponent;
class CComponentBox;
class CHeroTooltip;
class CTownTooltip;
class CLabel;
@ -31,10 +32,20 @@ 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 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:
@ -44,6 +55,9 @@ class CInfoBar : public CIntObject
CVisibleInfo();
};
static constexpr int data_width = width - 2 * CVisibleInfo::offset_x;
static constexpr int data_height = height - 2 * CVisibleInfo::offset_y;
class EmptyVisibleInfo : public CVisibleInfo
{
public:
@ -97,10 +111,24 @@ class CInfoBar : public CIntObject
class VisibleComponentInfo : public CVisibleInfo
{
std::shared_ptr<CComponent> comp;
std::shared_ptr<CComponentBox> comps;
std::shared_ptr<CTextBox> text;
public:
VisibleComponentInfo(const Component & compToDisplay, std::string message);
struct Cache
{
std::vector<Component> compsToDisplay;
std::string message;
int textH;
bool tiny;
Cache(std::vector<Component> 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<Component> & compsToDisplay, std::string message, int textH, bool tiny);
};
enum EState
@ -110,6 +138,15 @@ class CInfoBar : public CIntObject
std::shared_ptr<CVisibleInfo> visibleInfo;
EState state;
bool shouldPopAll = false;
std::queue<std::pair<VisibleComponentInfo::Cache, int>> componentsQueue;
//private helper for showing components
void showComponents(const std::vector<Component> & comps, std::string message, int textH, bool tiny, int timer);
void pushComponents(const std::vector<Component> & comps, std::string message, int textH, bool tiny, int timer);
void prepareComponents(const std::vector<Component> & comps, std::string message, int timer);
void popComponents(bool remove = false);
//removes all information about current state, deactivates timer (if any)
void reset();
@ -123,12 +160,19 @@ class CInfoBar : public CIntObject
void playNewDaySound();
public:
CInfoBar(const Rect & pos);
CInfoBar(const Point & pos);
/// 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 pushComponents(const std::vector<Component> & comps, std::string message, int timer = 3000);
/// 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);
@ -142,5 +186,11 @@ public:
/// for 3 seconds shows amount of town halls and players status
void showGameStatus();
/// check if infobar is showed something about pickups
bool showingComponents();
/// get estimated component height for InfoBar
int getEstimatedComponentHeight(int numComps) const;
};

View File

@ -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)
if(c.id == Component::EComponentType::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,20 @@ 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
std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(80, pos.w), font);
auto max = 80;
if (size < large)
max = 72;
if (size < medium)
max = 40;
if (size < small)
max = 30;
std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(max, pos.w), font);
for(auto & line : textLines)
{
int height = static_cast<int>(graphics->fonts[font]->getLineHeight());
@ -93,13 +101,13 @@ const std::vector<std::string> 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 creatureArr [] = {"CPRSMALL", "CPRSMALL", "TWCRPORT", "TWCRPORT"};
static const std::string resourceArr [] = {"SMALRES", "RESOURCE", "RESOURCE", "RESOUR82"};
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)
@ -136,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;
@ -336,9 +344,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 +353,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;
@ -384,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));
@ -450,7 +453,16 @@ void CComponentBox::placeComponents(bool selectable)
}
CComponentBox::CComponentBox(std::vector<std::shared_ptr<CComponent>> _components, Rect position):
components(_components)
CComponentBox(_components, position, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows, defaultComponentsInRow)
{
}
CComponentBox::CComponentBox(std::vector<std::shared_ptr<CComponent>> _components, Rect position, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows, int componentsInRow):
components(_components),
betweenImagesMin(betweenImagesMin),
betweenSubtitlesMin(betweenSubtitlesMin),
betweenRows(betweenRows),
componentsInRow(componentsInRow)
{
type |= REDRAW_PARENT;
pos = position + pos.topLeft();
@ -458,8 +470,17 @@ CComponentBox::CComponentBox(std::vector<std::shared_ptr<CComponent>> _component
}
CComponentBox::CComponentBox(std::vector<std::shared_ptr<CSelectableComponent>> _components, Rect position, std::function<void(int newID)> _onSelect):
CComponentBox(_components, position, _onSelect, defaultBetweenImagesMin, defaultBetweenSubtitlesMin, defaultBetweenRows, defaultComponentsInRow)
{
}
CComponentBox::CComponentBox(std::vector<std::shared_ptr<CSelectableComponent>> _components, Rect position, std::function<void(int newID)> _onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows, int componentsInRow):
components(_components.begin(), _components.end()),
onSelect(_onSelect)
onSelect(_onSelect),
betweenImagesMin(betweenImagesMin),
betweenSubtitlesMin(betweenSubtitlesMin),
betweenRows(betweenRows),
componentsInRow(componentsInRow)
{
type |= REDRAW_PARENT;
pos = position + pos.topLeft();

View File

@ -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<CAnimImage> 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
};
@ -94,6 +95,16 @@ class CComponentBox : public CIntObject
std::shared_ptr<CSelectableComponent> selected;
std::function<void(int newID)> onSelect;
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<CSelectableComponent> newSelection);
//get position of "or" text between these comps
@ -108,11 +119,13 @@ public:
/// return index of selected item
int selectedIndex();
/// constructor for non-selectable components
/// constructors for non-selectable components
CComponentBox(std::vector<std::shared_ptr<CComponent>> components, Rect position);
CComponentBox(std::vector<std::shared_ptr<CComponent>> 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<std::shared_ptr<CSelectableComponent>> components, Rect position, std::function<void(int newID)> onSelect = nullptr);
CComponentBox(std::vector<std::shared_ptr<CSelectableComponent>> components, Rect position, std::function<void(int newID)> onSelect, int betweenImagesMin, int betweenSubtitlesMin, int betweenRows, int componentsInRow);
};

View File

@ -270,7 +270,7 @@ void CArmyTooltip::init(const InfoAboutArmy &army)
}
}
subtitles.push_back(std::make_shared<CLabel>(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<CLabel>(slotsPos[slot.first.getNum()].x + 17, slotsPos[slot.first.getNum()].y + 39, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, subtitle));
}
}

View File

@ -202,6 +202,25 @@ std::vector<std::string> 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<int>(f->getLineHeight());
return lineHeight * (int)lines.size();
}
void CMessage::drawIWindow(CInfoWindow * ret, std::string text, PlayerColor player)
{
bool blitOr = false;

View File

@ -32,6 +32,12 @@ public:
/// split text in lines
static std::vector<std::string> 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

View File

@ -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<CToggleGroup> playerHeroSpeedToggle = widget<CToggleGroup>("heroMovementSpeedPicker");
@ -142,4 +146,6 @@ AdventureOptionsTab::AdventureOptionsTab()
#else
mapSwipeCheckbox->setSelected(settings["general"]["swipeDesktop"].Bool());
#endif
std::shared_ptr<CToggleButton> infoBarPickCheckbox = widget<CToggleButton>("infoBarPickCheckbox");
infoBarPickCheckbox->setSelected(settings["gameTweaks"]["infoBarPick"].Bool());
}

View File

@ -125,7 +125,7 @@
{ "heroLevel" : 4, "secondary" : { "diplomacy" : 3 } }
]
},
"message" : 59,
"message" : 66,
"primary" : {
"attack" : 2,
"defence" : 2,

View File

@ -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

View File

@ -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"
}
]
}

View File

@ -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;

View File

@ -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<Component> & components, int soundID){};
virtual void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & 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){};

View File

@ -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) {}

View File

@ -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;
@ -1150,6 +1145,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<Component> components;
PlayerColor player;
@ -1159,6 +1155,7 @@ struct DLL_LINKAGE InfoWindow : public CPackForClient //103 - displays simple i
template <typename Handler> void serialize(Handler & h, const int version)
{
h & type;
h & text;
h & components;
h & player;
@ -1873,22 +1870,6 @@ protected:
virtual void visitTyped(ICPackVisitor & visitor) override;
};
struct DLL_LINKAGE ShowInInfobox : public CPackForClient
{
PlayerColor player;
Component c;
MetaString text;
template <typename Handler> 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;

View File

@ -35,6 +35,27 @@ struct ArtSlotInfo;
class ICPackVisitor;
enum class EInfoWindowMode : uint8_t
{
AUTO,
MODAL,
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<CConnection> c; // Pointer to connection that pack received from
@ -176,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

View File

@ -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);
@ -2503,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)
{

View File

@ -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;
@ -189,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;
}
@ -200,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:
@ -224,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);
@ -234,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);
@ -278,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

View File

@ -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);
}
}
@ -876,6 +859,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);

View File

@ -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<EMarketMode::EMarketMode> 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<int> 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

View File

@ -25,22 +25,6 @@
VCMI_LIB_NAMESPACE_BEGIN
///helpers
static void showInfoDialog(const PlayerColor & playerID, const ui32 txtID, const ui16 soundID)
{
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)
{
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)
@ -60,6 +44,7 @@ void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
afterSuccessfulVisit();
InfoWindow iw;
iw.type = EInfoWindowMode::AUTO;
iw.player = h->getOwner();
bool changesPrimSkill = false;
@ -98,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<si32>(expVal), 0);
iw.components.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast<si32>(expVal), 0);
for(int i=0; i<primskills.size(); i++)
if(primskills[i])
iw.components.emplace_back(Component::PRIM_SKILL, i, primskills[i], 0);
iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, i, primskills[i], 0);
for(const auto & abilityData : unpossessedAbilities)
iw.components.emplace_back(Component::SEC_SKILL, abilityData.first, abilityData.second, 0);
iw.components.emplace_back(Component::EComponentType::SEC_SKILL, abilityData.first, abilityData.second, 0);
cb->showInfoDialog(&iw);
@ -139,6 +124,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
@ -158,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
@ -186,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);
}
@ -194,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(),"");
@ -205,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(),"");
@ -218,7 +204,7 @@ void CGPandoraBox::giveContentsAfterExp(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(!iw.components.empty())
{
@ -231,7 +217,7 @@ void CGPandoraBox::giveContentsAfterExp(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(!iw.components.empty())
{
@ -245,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);
@ -339,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()
@ -348,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

View File

@ -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;
@ -379,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);
}
}
@ -1731,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<int>(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:

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
@ -204,7 +188,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &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;
@ -215,7 +199,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &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]);
@ -226,13 +210,13 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &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;
@ -250,7 +234,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &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);
}
@ -278,7 +262,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &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);
@ -289,7 +273,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &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;
@ -662,34 +646,34 @@ void CGSeerHut::getCompletionText(MetaString &text, std::vector<Component> &comp
switch(rewardType)
{
case EXPERIENCE:
components.emplace_back(Component::EXPERIENCE, 0, static_cast<si32>(h->calculateXp(rVal)), 0);
components.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast<si32>(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;
}
}
@ -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());

View File

@ -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;
}
@ -230,6 +230,11 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando
configureResetInfo(object, rng, object->resetParameters, parameters["resetParameters"]);
object->canRefuse = parameters["canRefuse"].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)

View File

@ -134,13 +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());
// show message only if it is not empty
if (!vi.message.toString().empty())
// 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 = infoWindowType;
cb->showInfoDialog(&iw);
}
// grant reward afterwards. Note that it may remove object
@ -297,19 +298,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)
{
@ -442,39 +431,54 @@ void CRewardInfo::loadComponents(std::vector<Component> & comps,
if (heroExperience)
{
comps.emplace_back(Component::EXPERIENCE, 0, static_cast<si32>(h->calculateXp(heroExperience)), 0);
comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast<si32>(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, manaDiff, 0);
comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, calculateManaPoints(h) - h->mana, 0);
for (size_t i=0; i<primary.size(); i++)
{
if (primary[i] != 0)
comps.emplace_back(Component::PRIM_SKILL, static_cast<ui16>(i), primary[i], 0);
comps.emplace_back(Component::EComponentType::PRIM_SKILL, static_cast<ui16>(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<resources.size(); i++)
{
if (resources[i] !=0)
comps.emplace_back(Component::RESOURCE, static_cast<ui16>(i), resources[i], 0);
comps.emplace_back(Component::EComponentType::RESOURCE, static_cast<ui16>(i), resources[i], 0);
}
}
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<Component> comps;

View File

@ -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),
@ -304,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)
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
bool wasVisitedBefore(const CGHeroInstance * contextHero) const;
@ -349,6 +354,8 @@ public:
h & selectMode;
h & selectedReward;
h & onceVisitableObjectCleared;
if (version >= 817)
h & infoWindowType;
}
// for configuration/object setup

View File

@ -34,31 +34,6 @@ ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map
std::map<TeamID, ui8> 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;
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;
@ -385,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);
}
}
@ -587,18 +562,19 @@ 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())
{
iw.type = EInfoWindowMode::AUTO;
iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure
iw.text.addReplacement(h->getNameTranslated());
cb->showInfoDialog(&iw);
@ -740,10 +716,11 @@ 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;
iw.components.emplace_back(Component::RESOURCE, producedResource, producedQuantity, -1);
iw.components.emplace_back(Component::EComponentType::RESOURCE, producedResource, producedQuantity, -1);
cb->showInfoDialog(&iw);
}
@ -767,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);
}
@ -881,27 +858,28 @@ 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<Res::ERes>(subID), amount);
ShowInInfobox sii;
InfoWindow sii;
sii.player = player;
sii.c = Component(Component::RESOURCE,subID,amount,0);
sii.text.addTxt(MetaString::ADVOB_TXT,113);
sii.text.addReplacement(MetaString::RES_NAMES, subID);
cb->showCompInfo(&sii);
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::EComponentType::RESOURCE,subID,amount,0);
sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6);
cb->showInfoDialog(&sii);
cb->removeObject(this);
}
@ -1077,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);
}
@ -1133,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;
}
@ -1236,9 +1214,10 @@ 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));
iw.components.emplace_back(CStackBasicDescriptor(h->getCreature(targetstack), -countToTake));
cb->showInfoDialog(&iw);
cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
}
@ -1318,12 +1297,13 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
if(!stacksCount())
{
InfoWindow iw;
iw.type = EInfoWindowMode::AUTO;
iw.player = h->tempOwner;
switch(ID)
{
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
@ -1333,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
@ -1446,6 +1426,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());
@ -1460,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);
}
@ -1520,6 +1501,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)
{
@ -1562,6 +1544,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);
@ -1585,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);
@ -1679,6 +1662,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);
@ -1686,18 +1670,18 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
{
case PRIM_SKILL:
cb->changePrimSkill(h,static_cast<PrimarySkill::PrimarySkill>(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<SpellID> 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:
@ -1851,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())
{
@ -1881,7 +1865,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
}
else if (ID == Obj::EYE_OF_MAGI)
{
showInfoDialog(h, 48);
h->showInfoDialog(48);
}
}
@ -1906,6 +1890,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
@ -1971,13 +1956,14 @@ 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);
}
else
{
openWindow(OpenWindow::SHIPYARD_WINDOW,id.getNum(),h->id.getNum());
openWindow(EOpenWindowMode::SHIPYARD_WINDOW,id.getNum(),h->id.getNum());
}
}
@ -2017,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);
}
}
@ -2067,6 +2053,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);
@ -2080,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)
@ -2141,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

View File

@ -243,7 +243,6 @@ void registerTypesClientPacks1(Serializer &s)
s.template registerType<CPackForClient, NewTurn>();
s.template registerType<CPackForClient, InfoWindow>();
s.template registerType<CPackForClient, SetObjectProperty>();
s.template registerType<CPackForClient, ShowInInfobox>();
s.template registerType<CPackForClient, AdvmapSpellCast>();
s.template registerType<CPackForClient, OpenWindow>();
s.template registerType<CPackForClient, NewObject>();

View File

@ -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";

View File

@ -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);

View File

@ -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);
@ -2657,10 +2657,6 @@ void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStac
}
}
void CGameHandler::showCompInfo(ShowInInfobox * comp)
{
sendToAllClients(comp);
}
void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)
{
HeroVisitCastle vc;
@ -2822,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());
@ -2833,7 +2829,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
int size = static_cast<int>(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--)
{
@ -2858,7 +2854,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
int size = static_cast<int>(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--)
{
@ -5582,7 +5578,7 @@ void CGameHandler::handleTimeEvents()
for (int i=0; i<ev.resources.size(); i++)
{
if (ev.resources.at(i)) //if resource is changed, we add it to the dialog
iw.components.push_back(Component(Component::RESOURCE,i,ev.resources.at(i),0));
iw.components.emplace_back(Component::EComponentType::RESOURCE,i,ev.resources.at(i),0);
}
sendAndApply(&iw); //show dialog
@ -5640,7 +5636,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n)
for (int i=0; i<ev.resources.size(); i++)
if (ev.resources.at(i) && pinfo->resources.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);
}
@ -5649,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);
}
}
@ -5665,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
@ -5725,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);
@ -6028,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)
@ -6050,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())
{
@ -6060,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);

View File

@ -184,7 +184,6 @@ public:
bool bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap);
void synchronizeArtifactHandlerLists();
void showCompInfo(ShowInInfobox * comp) override;
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override;
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override;
void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = nullptr) override; //use hero=nullptr for no hero

View File

@ -70,7 +70,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 stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {}
void visitCastleObjects(const CGTownInstance * obj, const CGHeroInstance * hero) override {}