Sfoglia il codice sorgente

Merge pull request #2338 from dydzio0614/info-box-army-management

Info box army management
Ivan Savenko 2 anni fa
parent
commit
7b06c41929

+ 2 - 0
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": "",

+ 7 - 0
client/CPlayerInterface.cpp

@@ -572,7 +572,14 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CGObjectInstance *> ob
 		auto * town = dynamic_cast<const CGTownInstance*>(object);
 
 		if (hero)
+		{
 			adventureInt->onHeroChanged(hero);
+
+			if(hero->inTownGarrison)
+			{
+				adventureInt->onTownChanged(hero->visitedTown);
+			}
+		}
 		if (town)
 			adventureInt->onTownChanged(town);
 	}

+ 26 - 3
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<CPicture>("ADSTATHR");
-	heroTooltip = std::make_shared<CHeroTooltip>(Point(0,0), hero);
+
+	if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool())
+		heroTooltip = std::make_shared<CInteractableHeroTooltip>(Point(0,0), hero);
+	else
+		heroTooltip = std::make_shared<CHeroTooltip>(Point(0,0), hero);
 }
 
 CInfoBar::VisibleTownInfo::VisibleTownInfo(const CGTownInstance * town)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	background = std::make_shared<CPicture>("ADSTATCS");
-	townTooltip = std::make_shared<CTownTooltip>(Point(0,0), town);
+
+	if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool())
+		townTooltip = std::make_shared<CInteractableTownTooltip>(Point(0,0), town);
+	else
+		townTooltip = std::make_shared<CTownTooltip>(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)
 {

+ 9 - 2
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<CHeroTooltip> heroTooltip;
+		std::shared_ptr<CIntObject> heroTooltip; //should have CHeroTooltip or CInteractableHeroTooltip;
 	public:
 		VisibleHeroInfo(const CGHeroInstance * hero);
 	};
 
 	class VisibleTownInfo : public CVisibleInfo
 	{
-		std::shared_ptr<CTownTooltip> townTooltip;
+		std::shared_ptr<CIntObject> 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<std::pair<VisibleComponentInfo::Cache, int>> 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();
 };
 

+ 38 - 14
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<CLabel>(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<CLabel>(labelPosW, labelPosH, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, labelAlignment, Colors::WHITE);
 
 	update();
 }
@@ -488,7 +500,7 @@ void CGarrisonInt::addSplitBtn(std::shared_ptr<CButton> 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<std::shared_ptr<CGarrisonSlot>> garrisonSlots;
 		garrisonSlots.resize(7);
@@ -499,14 +511,26 @@ void CGarrisonInt::createSlots()
 				garrisonSlots[elem.first.getNum()] = std::make_shared<CGarrisonSlot>(this, i*garOffset.x + (elem.first.getNum()*distance), i*garOffset.y, elem.first, static_cast<CGarrisonSlot::EGarrisonType>(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<CGarrisonSlot>(this, i*garOffset.x + (j*distance), i*garOffset.y, SlotID(j), static_cast<CGarrisonSlot::EGarrisonType>(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);
 

+ 15 - 7
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<std::shared_ptr<CButton>> 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

+ 74 - 0
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<CAnimImage>("PortraitsLarge", hero.portrait, 0, 3, 2-73);
+	title = std::make_shared<CLabel>(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<CLabel>(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<CLabel>(158, 98-73, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana)));
+
+		morale = std::make_shared<CAnimImage>("IMRL22", hero.details->morale + 3, 0, 5, 74-73);
+		luck = std::make_shared<CAnimImage>("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<CAnimImage>("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<CAnimImage>("itpt", iconIndex, 0, 3, 2-73);
+	title = std::make_shared<CLabel>(66, 2-73, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, town.name);
+
+	if(town.details)
+	{
+		hall = std::make_shared<CAnimImage>("ITMTLS", town.details->hallLevel, 0, 67, 31-73);
+
+		if(town.details->goldIncome)
+		{
+			income = std::make_shared<CLabel>(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<CPicture>("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<CAnimImage>("SMALRES", GameResID(EGameResID::WOOD), 0, 7, 75-73);
+				res2 = std::make_shared<CAnimImage>("SMALRES", GameResID(EGameResID::ORE), 0, 7, 88-73);
+			}
+			else
+			{
+				res1 = std::make_shared<CAnimImage>("SMALRES", town.tType->primaryRes, 0, 7, 81-73);
+			}
+		}
+	}
+}
+
 void MoraleLuckBox::set(const AFactionMember * node)
 {
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);

+ 32 - 0
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<CLabel> title;
+	std::shared_ptr<CAnimImage> portrait;
+	std::vector<std::shared_ptr<CLabel>> labels;
+	std::shared_ptr<CAnimImage> morale;
+	std::shared_ptr<CAnimImage> 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<CLabel> title;
+	std::shared_ptr<CAnimImage> fort;
+	std::shared_ptr<CAnimImage> hall;
+	std::shared_ptr<CAnimImage> build;
+	std::shared_ptr<CLabel> income;
+	std::shared_ptr<CPicture> garrisonedHero;
+	std::shared_ptr<CAnimImage> res1;
+	std::shared_ptr<CAnimImage> 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
 {

+ 1 - 1
client/windows/CKingdomInterface.cpp

@@ -772,7 +772,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
 	hall = std::make_shared<CTownInfo>( 69, 31, town, true);
 	fort = std::make_shared<CTownInfo>(111, 31, town, false);
 
-	garr = std::make_shared<CGarrisonInt>(313, 3, 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, true);
+	garr = std::make_shared<CGarrisonInt>(313, 3, 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, CGarrisonInt::ESlotsLayout::TWO_ROWS);
 	heroes = std::make_shared<HeroSlots>(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)];

+ 7 - 0
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<CToggleButton> borderScrollCheckbox = widget<CToggleButton>("borderScrollCheckbox");
 	borderScrollCheckbox->setSelected(settings["adventure"]["borderScroll"].Bool());
 
+	std::shared_ptr<CToggleButton> infoBarCreatureManagementCheckbox = widget<CToggleButton>("infoBarCreatureManagementCheckbox");
+	infoBarCreatureManagementCheckbox->setSelected(settings["gameTweaks"]["infoBarCreatureManagement"].Bool());
+
 	std::shared_ptr<CToggleButton> leftButtonDragCheckbox = widget<CToggleButton>("leftButtonDragCheckbox");
 	if (leftButtonDragCheckbox)
 		leftButtonDragCheckbox->setSelected(settings["adventure"]["leftButtonDrag"].Bool());

+ 6 - 1
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
 				}
 			}
 		}

+ 5 - 0
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",