diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 34e9e36b6..8be4b1b3f 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -87,6 +87,8 @@ "vcmi.adventureOptions.showGrid.help" : "{Show Grid}\n\nShow the grid overlay, highlighting the borders between adventure map tiles.", "vcmi.adventureOptions.borderScroll.hover" : "Border Scrolling", "vcmi.adventureOptions.borderScroll.help" : "{Border Scrolling}\n\nScroll adventure map when cursor is adjacent to window edge. Can be disabled by holding down CTRL key.", + "vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Info Panel Creature Management", + "vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info Panel Creature Management}\n\nAllows rearranging creatures in info panel instead of cycling between default components", "vcmi.adventureOptions.leftButtonDrag.hover" : "Left Click Drag Map", "vcmi.adventureOptions.leftButtonDrag.help" : "{Left Click Drag Map}\n\nWhen enabled, moving mouse with left button pressed will drag adventure map view", "vcmi.adventureOptions.mapScrollSpeed1.hover": "", diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 6d62e66d3..7d6be81d4 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -572,7 +572,14 @@ void CPlayerInterface::garrisonsChanged(std::vector ob auto * town = dynamic_cast(object); if (hero) + { adventureInt->onHeroChanged(hero); + + if(hero->inTownGarrison) + { + adventureInt->onTownChanged(hero->visitedTown); + } + } if (town) adventureInt->onTownChanged(town); } diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index dc34df6bb..7f5226d46 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -27,6 +27,7 @@ #include "../gui/WindowHandler.h" #include "../../CCallback.h" +#include "../../lib/CConfigHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGTownInstance.h" @@ -51,14 +52,22 @@ CInfoBar::VisibleHeroInfo::VisibleHeroInfo(const CGHeroInstance * hero) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATHR"); - heroTooltip = std::make_shared(Point(0,0), hero); + + if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()) + heroTooltip = std::make_shared(Point(0,0), hero); + else + heroTooltip = std::make_shared(Point(0,0), hero); } CInfoBar::VisibleTownInfo::VisibleTownInfo(const CGTownInstance * town) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATCS"); - townTooltip = std::make_shared(Point(0,0), town); + + if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()) + townTooltip = std::make_shared(Point(0,0), town); + else + townTooltip = std::make_shared(Point(0,0), town); } CInfoBar::VisibleDateInfo::VisibleDateInfo() @@ -273,8 +282,16 @@ void CInfoBar::tick(uint32_t msPassed) void CInfoBar::clickReleased(const Point & cursorPosition) { + timerCounter = 0; + removeUsedEvents(TIME); //expiration trigger from just clicked element is not valid anymore + if(state == HERO || state == TOWN) + { + if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()) + return; + showGameStatus(); + } else if(state == GAME) showDate(); else @@ -297,11 +314,13 @@ void CInfoBar::hover(bool on) CInfoBar::CInfoBar(const Rect & position) : CIntObject(LCLICK | SHOW_POPUP | HOVER, position.topLeft()), timerCounter(0), - state(EMPTY) + state(EMPTY), + listener(settings.listen["gameTweaks"]["infoBarCreatureManagement"]) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); pos.w = position.w; pos.h = position.h; + listener(std::bind(&CInfoBar::OnInfoBarCreatureManagementChanged, this)); reset(); } @@ -309,6 +328,10 @@ CInfoBar::CInfoBar(const Point & position): CInfoBar(Rect(position.x, position.y { } +void CInfoBar::OnInfoBarCreatureManagementChanged() +{ + showSelection(); +} void CInfoBar::setTimer(uint32_t msToTrigger) { diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 07261e1d5..c69fed1f0 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -10,6 +10,7 @@ #pragma once #include "../gui/CIntObject.h" +#include "CConfigHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -25,7 +26,9 @@ class CShowableAnim; class CComponent; class CComponentBox; class CHeroTooltip; +class CInteractableHeroTooltip; class CTownTooltip; +class CInteractableTownTooltip; class CLabel; class CMultiLineLabel; @@ -66,14 +69,14 @@ private: class VisibleHeroInfo : public CVisibleInfo { - std::shared_ptr heroTooltip; + std::shared_ptr heroTooltip; //should have CHeroTooltip or CInteractableHeroTooltip; public: VisibleHeroInfo(const CGHeroInstance * hero); }; class VisibleTownInfo : public CVisibleInfo { - std::shared_ptr townTooltip; + std::shared_ptr townTooltip; //should have CTownTooltip or CInteractableTownTooltip; public: VisibleTownInfo(const CGTownInstance * town); }; @@ -140,6 +143,7 @@ private: EState state; uint32_t timerCounter; bool shouldPopAll = false; + SettingsListener listener; std::queue> componentsQueue; @@ -191,5 +195,8 @@ public: /// check if infobar is showed something about pickups bool showingComponents(); + + /// event handler for custom listening on game setting change + void OnInfoBarCreatureManagementChanged(); }; diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index 9725017e5..95c068a54 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -410,7 +410,19 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, CGa pos.h = 64; } - stackCount = std::make_shared(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, ETextAlignment::BOTTOMRIGHT, Colors::WHITE); + int labelPosW = pos.w; + int labelPosH = pos.h; + + if(Owner->layout == CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS) //labels under icon + { + labelPosW = pos.w / 2 + 1; + labelPosH += 7; + } + ETextAlignment labelAlignment = Owner->layout == CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS + ? ETextAlignment::CENTER + : ETextAlignment::BOTTOMRIGHT; + + stackCount = std::make_shared(labelPosW, labelPosH, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, labelAlignment, Colors::WHITE); update(); } @@ -488,7 +500,7 @@ void CGarrisonInt::addSplitBtn(std::shared_ptr button) void CGarrisonInt::createSlots() { int distance = interx + (smallIcons ? 32 : 58); - for(int i=0; i<2; i++) + for(int i = 0; i < 2; i++) { std::vector> garrisonSlots; garrisonSlots.resize(7); @@ -499,14 +511,26 @@ void CGarrisonInt::createSlots() garrisonSlots[elem.first.getNum()] = std::make_shared(this, i*garOffset.x + (elem.first.getNum()*distance), i*garOffset.y, elem.first, static_cast(i), elem.second); } } - for(int j=0; j<7; j++) + for(int j = 0; j < 7; j++) { if(!garrisonSlots[j]) garrisonSlots[j] = std::make_shared(this, i*garOffset.x + (j*distance), i*garOffset.y, SlotID(j), static_cast(i), nullptr); - if(twoRows && j>=4) + + if(layout == ESlotsLayout::TWO_ROWS && j >= 4) { garrisonSlots[j]->moveBy(Point(-126, 37)); } + else if(layout == ESlotsLayout::REVERSED_TWO_ROWS) + { + if(j >= 3) + { + garrisonSlots[j]->moveBy(Point(-90, 49)); + } + else + { + garrisonSlots[j]->moveBy(Point(36, 0)); + } + } } vstd::concatenate(availableSlots, garrisonSlots); } @@ -646,16 +670,16 @@ void CGarrisonInt::bulkSmartSplitStack(const CGarrisonSlot * selected) } CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset, - const CArmedInstance * s1, const CArmedInstance * s2, - bool _removableUnits, bool smallImgs, bool _twoRows) - : highlighted(nullptr), - inSplittingMode(false), - interx(inx), - garOffset(garsOffset), - pb(false), - smallIcons(smallImgs), - removableUnits(_removableUnits), - twoRows(_twoRows) + const CArmedInstance * s1, const CArmedInstance * s2, + bool _removableUnits, bool smallImgs, ESlotsLayout _layout) + : highlighted(nullptr), + inSplittingMode(false), + interx(inx), + garOffset(garsOffset), + pb(false), + smallIcons(smallImgs), + removableUnits(_removableUnits), + layout(_layout) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); diff --git a/client/widgets/CGarrisonInt.h b/client/widgets/CGarrisonInt.h index 975fa5139..baa1360ca 100644 --- a/client/widgets/CGarrisonInt.h +++ b/client/widgets/CGarrisonInt.h @@ -81,6 +81,13 @@ class CGarrisonInt :public CIntObject bool checkSelected(const CGarrisonSlot * selected, TQuantity min = 0) const; public: + enum class ESlotsLayout + { + ONE_ROW, + TWO_ROWS, + REVERSED_TWO_ROWS + }; + int interx; ///< Space between slots Point garOffset; ///< Offset between garrisons (not used if only one hero) std::vector> splitButtons; ///< May be empty if no buttons @@ -89,9 +96,10 @@ public: bool pb, smallIcons, ///< true - 32x32 imgs, false - 58x64 removableUnits, ///< player Can remove units from up - twoRows, ///< slots Will be placed in 2 rows owned[2]; ///< player Owns up or down army ([0] upper, [1] lower) + ESlotsLayout layout; + void selectSlot(CGarrisonSlot * slot); ///< @param slot null = deselect const CGarrisonSlot * getSelection() const; @@ -123,13 +131,13 @@ public: /// @param s1, s2 Top and bottom armies /// @param _removableUnits You can take units from top /// @param smallImgs Units images size 64x58 or 32x32 - /// @param _twoRows Display slots in 2 row (1st row = 4 slots, 2nd = 3 slots) + /// @param _layout - when TWO_ROWS - Display slots in 2 rows (1st row = 4 slots, 2nd = 3 slots), REVERSED_TWO_ROWS = 3 slots in 1st row CGarrisonInt(int x, int y, int inx, - const Point & garsOffset, - const CArmedInstance * s1, const CArmedInstance * s2 = nullptr, - bool _removableUnits = true, - bool smallImgs = false, - bool _twoRows = false); + const Point & garsOffset, + const CArmedInstance * s1, const CArmedInstance * s2 = nullptr, + bool _removableUnits = true, + bool smallImgs = false, + ESlotsLayout _layout = ESlotsLayout::ONE_ROW); }; class CGarrisonHolder diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 7e97edbbf..9ef040193 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -303,6 +303,31 @@ CHeroTooltip::CHeroTooltip(Point pos, const CGHeroInstance * hero): init(InfoAboutHero(hero, InfoAboutHero::EInfoLevel::DETAILED)); } +CInteractableHeroTooltip::CInteractableHeroTooltip(Point pos, const CGHeroInstance * hero): + CGarrisonInt(pos.x, pos.y+73, 4, Point(0, 0), hero, nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS) +{ + init(InfoAboutHero(hero, InfoAboutHero::EInfoLevel::DETAILED)); +} + +void CInteractableHeroTooltip::init(const InfoAboutHero & hero) +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + portrait = std::make_shared("PortraitsLarge", hero.portrait, 0, 3, 2-73); + title = std::make_shared(66, 2-73, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, hero.name); + + if(hero.details) + { + for(size_t i = 0; i < hero.details->primskills.size(); i++) + labels.push_back(std::make_shared(75 + 28 * (int)i, 58-73, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, + std::to_string(hero.details->primskills[i]))); + + labels.push_back(std::make_shared(158, 98-73, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana))); + + morale = std::make_shared("IMRL22", hero.details->morale + 3, 0, 5, 74-73); + luck = std::make_shared("ILCK22", hero.details->luck + 3, 0, 5, 91-73); + } +} + void CTownTooltip::init(const InfoAboutTown & town) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -357,6 +382,55 @@ CTownTooltip::CTownTooltip(Point pos, const CGTownInstance * town) init(InfoAboutTown(town, true)); } +CInteractableTownTooltip::CInteractableTownTooltip(Point pos, const CGTownInstance * town) + : CGarrisonInt(pos.x, pos.y+73, 4, Point(0, 0), town->getUpperArmy(), nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS) +{ + init(InfoAboutTown(town, true)); +} + +void CInteractableTownTooltip::init(const InfoAboutTown & town) +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + //order of icons in def: fort, citadel, castle, no fort + size_t fortIndex = town.fortLevel ? town.fortLevel - 1 : 3; + + fort = std::make_shared("ITMCLS", fortIndex, 0, 105, 31-73); + + assert(town.tType); + + size_t iconIndex = town.tType->clientInfo.icons[town.fortLevel > 0][town.built >= CGI->settings()->getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)]; + + build = std::make_shared("itpt", iconIndex, 0, 3, 2-73); + title = std::make_shared(66, 2-73, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, town.name); + + if(town.details) + { + hall = std::make_shared("ITMTLS", town.details->hallLevel, 0, 67, 31-73); + + if(town.details->goldIncome) + { + income = std::make_shared(157, 58-73, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, + std::to_string(town.details->goldIncome)); + } + if(town.details->garrisonedHero) //garrisoned hero icon + garrisonedHero = std::make_shared("TOWNQKGH", 149, 76-73); + + if(town.details->customRes)//silo is built + { + if(town.tType->primaryRes == EGameResID::WOOD_AND_ORE )// wood & ore + { + res1 = std::make_shared("SMALRES", GameResID(EGameResID::WOOD), 0, 7, 75-73); + res2 = std::make_shared("SMALRES", GameResID(EGameResID::ORE), 0, 7, 88-73); + } + else + { + res1 = std::make_shared("SMALRES", town.tType->primaryRes, 0, 7, 81-73); + } + } + } +} + void MoraleLuckBox::set(const AFactionMember * node) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); diff --git a/client/widgets/MiscWidgets.h b/client/widgets/MiscWidgets.h index d19f191e5..56d31b643 100644 --- a/client/widgets/MiscWidgets.h +++ b/client/widgets/MiscWidgets.h @@ -10,6 +10,7 @@ #pragma once #include "../gui/CIntObject.h" +#include "CGarrisonInt.h" VCMI_LIB_NAMESPACE_BEGIN @@ -80,6 +81,20 @@ public: CHeroTooltip(Point pos, const CGHeroInstance * hero); }; +/// Class for HD mod-like interactable infobox tooltip. Does not have any background! +class CInteractableHeroTooltip : public CGarrisonInt +{ + std::shared_ptr title; + std::shared_ptr portrait; + std::vector> labels; + std::shared_ptr morale; + std::shared_ptr luck; + + void init(const InfoAboutHero & hero); +public: + CInteractableHeroTooltip(Point pos, const CGHeroInstance * hero); +}; + /// Class for town tooltip. Does not have any background! /// background for infoBox: ADSTATCS /// background for tooltip: TOWNQVBK @@ -99,6 +114,23 @@ public: CTownTooltip(Point pos, const CGTownInstance * town); }; +/// Class for HD mod-like interactable infobox tooltip. Does not have any background! +class CInteractableTownTooltip : public CGarrisonInt +{ + std::shared_ptr title; + std::shared_ptr fort; + std::shared_ptr hall; + std::shared_ptr build; + std::shared_ptr income; + std::shared_ptr garrisonedHero; + std::shared_ptr res1; + std::shared_ptr res2; + + void init(const InfoAboutTown & town); +public: + CInteractableTownTooltip(Point pos, const CGTownInstance * town); +}; + /// draws picture with creature on background, use Animated=true to get animation class CCreaturePic : public CIntObject { diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 1369ad42c..9e1a07dde 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -772,7 +772,7 @@ CTownItem::CTownItem(const CGTownInstance * Town) hall = std::make_shared( 69, 31, town, true); fort = std::make_shared(111, 31, town, false); - garr = std::make_shared(313, 3, 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, true); + garr = std::make_shared(313, 3, 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, CGarrisonInt::ESlotsLayout::TWO_ROWS); heroes = std::make_shared(town, Point(244,6), Point(475,6), garr, false); size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->settings()->getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)]; diff --git a/client/windows/settings/AdventureOptionsTab.cpp b/client/windows/settings/AdventureOptionsTab.cpp index 6d212ed29..499858037 100644 --- a/client/windows/settings/AdventureOptionsTab.cpp +++ b/client/windows/settings/AdventureOptionsTab.cpp @@ -118,6 +118,10 @@ AdventureOptionsTab::AdventureOptionsTab() { return setBoolSetting("adventure", "borderScroll", value); }); + addCallback("infoBarCreatureManagementChanged", [](bool value) + { + return setBoolSetting("gameTweaks", "infoBarCreatureManagement", value); + }); addCallback("leftButtonDragChanged", [](bool value) { return setBoolSetting("adventure", "leftButtonDrag", value); @@ -154,6 +158,9 @@ AdventureOptionsTab::AdventureOptionsTab() std::shared_ptr borderScrollCheckbox = widget("borderScrollCheckbox"); borderScrollCheckbox->setSelected(settings["adventure"]["borderScroll"].Bool()); + std::shared_ptr infoBarCreatureManagementCheckbox = widget("infoBarCreatureManagementCheckbox"); + infoBarCreatureManagementCheckbox->setSelected(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()); + std::shared_ptr leftButtonDragCheckbox = widget("leftButtonDragCheckbox"); if (leftButtonDragCheckbox) leftButtonDragCheckbox->setSelected(settings["adventure"]["leftButtonDrag"].Bool()); diff --git a/config/schemas/settings.json b/config/schemas/settings.json index c562d6a87..34a2d1631 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -503,7 +503,8 @@ "availableCreaturesAsDwellingLabel", "compactTownCreatureInfo", "infoBarPick", - "skipBattleIntroMusic" + "skipBattleIntroMusic", + "infoBarCreatureManagement" ], "properties" : { "showGrid" : { @@ -533,6 +534,10 @@ "skipBattleIntroMusic" : { "type" : "boolean", "default" : false + }, + "infoBarCreatureManagement": { + "type" : "boolean", + "default" : false } } } diff --git a/config/widgets/settings/adventureOptionsTab.json b/config/widgets/settings/adventureOptionsTab.json index 358d9aeef..ecd3e753f 100644 --- a/config/widgets/settings/adventureOptionsTab.json +++ b/config/widgets/settings/adventureOptionsTab.json @@ -378,6 +378,11 @@ "help": "vcmi.adventureOptions.borderScroll", "callback": "borderScrollChanged" }, + { + "name": "infoBarCreatureManagementCheckbox", + "help": "vcmi.adventureOptions.infoBarCreatureManagement", + "callback": "infoBarCreatureManagementChanged" + }, { "name": "leftButtonDragCheckbox", "help": "vcmi.adventureOptions.leftButtonDrag",