瀏覽代碼

vcmi: actually implement AUTO for CInfoWindow

This is possible to use infobar interactions now.
Konstantin 2 年之前
父節點
當前提交
a0c644a0e5

+ 2 - 0
Mods/vcmi/config/vcmi/english.json

@@ -47,6 +47,8 @@
 	"vcmi.systemOptions.framerateButton.hover"  : "Show FPS",
 	"vcmi.systemOptions.framerateButton.help"   : "{Show FPS}\n\n Toggles visibility of Frames Per Second counter in corner of game window.",
 
+	"vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel",
+	"vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in info bar instead of showing up as popup windows",
 	"vcmi.adventureOptions.numericQuantities.hover" : "Numeric Creature Quantities",
 	"vcmi.adventureOptions.numericQuantities.help" : "{Numeric Creature Quantities}\n\n Shows approximate enemy creatures quantities in numeric A-B format.",
 	"vcmi.adventureOptions.forceMovementInfo.hover" : "Always Show Movement Cost",

+ 2 - 0
Mods/vcmi/config/vcmi/russian.json

@@ -47,6 +47,8 @@
 	"vcmi.systemOptions.framerateButton.hover"  : "Показывать частоту кадров",
 	"vcmi.systemOptions.framerateButton.help"   : "{Показывать частоту кадров}\n\n Включить счетчик частоты кадров в углу игрового клиента",
 
+	"vcmi.adventureOptions.infoBarPick.hover" : "Сообщения в информационной панели",
+	"vcmi.adventureOptions.infoBarPick.help" : "{Сообщения в информационной панели}\n\n Если сообщения помещаются, то показывать их в информационной панели (только на интерфейсе карты).",
 	"vcmi.adventureOptions.numericQuantities.hover" : "Приблизительное число существ",
 	"vcmi.adventureOptions.numericQuantities.help" : "{Приблизительное число существ}\n\n Показывать приблизительное число существ в формате A-B вместо словесный обозначений",
 	"vcmi.adventureOptions.forceMovementInfo.hover" : "Всегда показывать стоимость перемещения",

+ 12 - 5
client/CPlayerInterface.cpp

@@ -1034,11 +1034,18 @@ void CPlayerInterface::yourTacticPhase(int distance)
 void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &text, const std::vector<Component> & components, int soundID)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	if(type == InfoWindow::INFO) {
-		adventureInt->infoBar->showComponents(components, text);
-		if (makingTurn && GH.listInt.size() && LOCPLINT == this)
-			CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
-		return;
+
+	bool autoTryHover = settings["gameTweaks"]["infoBarPick"].Bool() && type == EInfoWindowMode::AUTO;
+	auto timer = type == EInfoWindowMode::INFO ? 3000 : 4500; //Implement long info windows like in HD mod
+
+	if(autoTryHover || type == EInfoWindowMode::INFO)
+	{
+		if(adventureInt->infoBar->tryShowComponents(components, text, timer))
+		{
+			if (makingTurn && GH.listInt.size() && LOCPLINT == this)
+				CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
+			return;
+		}
 	}
 
 	if (settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())

+ 57 - 10
client/adventureMap/CInfoBar.cpp

@@ -15,6 +15,7 @@
 
 #include "../widgets/CComponent.h"
 #include "../widgets/Images.h"
+#include "../windows/CMessage.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/MiscWidgets.h"
 #include "../windows/InfoWindows.h"
@@ -29,7 +30,7 @@
 #include "../../lib/mapObjects/CGTownInstance.h"
 
 CInfoBar::CVisibleInfo::CVisibleInfo()
-	: CIntObject(0, Point(8, 12))
+	: CIntObject(0, Point(offset_x, offset_y))
 {
 }
 
@@ -163,24 +164,31 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo()
 	}
 }
 
-CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector<Component> & compsToDisplay, 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(4, 4, 171, 171);
+	auto fullRect = Rect(0, 0, data_width, data_height);
 	auto textRect = fullRect;
 	auto imageRect = fullRect;
+	auto font = FONT_SMALL;
 
 	if(!compsToDisplay.empty())
 	{
 		auto size = CComponent::large;
 		if(compsToDisplay.size() > 2)
 			size = CComponent::small;
-		if(message != "")
+		if(!message.empty())
 		{
-			textRect = Rect(4, 4, 171, 42);
-			imageRect = Rect(4, 42, 171, 121);
+			textRect = Rect(CInfoBar::offset,
+							CInfoBar::offset,
+							data_width - 2 * CInfoBar::offset,
+							textH);
+			imageRect = Rect(CInfoBar::offset,
+							 textH + CInfoBar::offset,
+							 data_width - 2 * CInfoBar::offset,
+							 CInfoBar::data_height - 2* CInfoBar::offset - textH);
 			if(compsToDisplay.size() > 4)
 				size = CComponent::tiny;
 		}
@@ -194,8 +202,24 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector<Component
 
 		comps = std::make_shared<CComponentBox>(vect, imageRect, 4, 4, 1);
 	}
+	else
+		font = tiny ? FONT_TINY : font;
 
-	text = std::make_shared<CTextBox>(message, textRect, 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
+	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 > 4)
+		return 48 + 20; // 24px * 2 rows + 20 to offset
+	else if (numComps > 2)
+		return 32 + 20; // 32px * 1 row + 20 to offset
+	else if (numComps)
+		return 128; // 128 px to offset
+	return 0;
 }
 
 void CInfoBar::playNewDaySound()
@@ -293,13 +317,36 @@ void CInfoBar::showDate()
 	redraw();
 }
 
-void CInfoBar::showComponents(const std::vector<Component> & comps, std::string message)
+bool CInfoBar::tryShowComponents(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);
+
+	// Order matters - priority form should be chosen first
+	if(imageH + textH < CInfoBar::data_height)
+		showComponents(components, message, textH, false, timer);
+	else if(!imageH && tinyH < CInfoBar::data_height)
+		showComponents(components, message, tinyH, true, timer);
+	else if(imageH + headerH < CInfoBar::data_height)
+		showComponents(components, header, headerH, false, timer);
+	else if(imageH < CInfoBar::data_height)
+		showComponents(components, "", 0, false, timer);
+	else
+		return false; //We cannot fit message to infobar, fallback to window
+
+	return true;
+}
+
+void CInfoBar::showComponents(const std::vector<Component> & comps, std::string message, int textH, bool tiny, int timer)
 {
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
 	state = COMPONENT;
-	visibleInfo = std::make_shared<VisibleComponentInfo>(comps, message);
+	visibleInfo = std::make_shared<VisibleComponentInfo>(comps, message, textH, tiny);
 
-	setTimer(3000);
+	setTimer(timer);
 	redraw();
 }
 

+ 15 - 3
client/adventureMap/CInfoBar.h

@@ -37,12 +37,15 @@ private:
 	/// Declare before to compute correct size of widgets
 	static constexpr int width = 192;
 	static constexpr int height = 192;
-	static constexpr int component_offset = 8;
+	static constexpr int offset = 4;
 
 	//all visible information located in one object - for ease of replacing
 	class CVisibleInfo : public CIntObject
 	{
 	public:
+		static constexpr int offset_x = 8;
+		static constexpr int offset_y = 12;
+
 		void show(SDL_Surface * to) override;
 
 	protected:
@@ -52,6 +55,9 @@ private:
 		CVisibleInfo();
 	};
 
+	static constexpr int data_width = width - 2 * CVisibleInfo::offset_x;
+	static constexpr int data_height = height - 2 * CVisibleInfo::offset_y + 4; //yes, +4 is required
+
 	class EmptyVisibleInfo : public CVisibleInfo
 	{
 	public:
@@ -108,7 +114,7 @@ private:
 		std::shared_ptr<CComponentBox> comps;
 		std::shared_ptr<CTextBox> text;
 	public:
-		VisibleComponentInfo(const std::vector<Component> & compsToDisplay, std::string message);
+		VisibleComponentInfo(const std::vector<Component> & compsToDisplay, std::string message, int textH, bool tiny);
 	};
 
 	enum EState
@@ -119,6 +125,9 @@ private:
 	std::shared_ptr<CVisibleInfo> visibleInfo;
 	EState state;
 
+	//private helper for showing components
+	void showComponents(const std::vector<Component> & comps, std::string message, int textH, bool tiny = false, int timer = 3000);
+
 	//removes all information about current state, deactivates timer (if any)
 	void reset();
 
@@ -137,7 +146,7 @@ public:
 	void showDate();
 
 	/// show components for 3 seconds. Used to display picked up resources. Can display up to 8 components
-	void showComponents(const std::vector<Component> & comps, std::string message);
+	bool tryShowComponents(const std::vector<Component> & comps, std::string message, int timer = 3000);
 
 	/// print enemy turn progress
 	void startEnemyTurn(PlayerColor color);
@@ -154,5 +163,8 @@ public:
 
 	/// check if infobar is showed something about pickups
 	bool showingComponents();
+
+	/// get estimated component height for InfoBar
+	int getEstimatedComponentHeight(int numComps) const;
 };
 

+ 19 - 0
client/windows/CMessage.cpp

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

+ 6 - 0
client/windows/CMessage.h

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

+ 8 - 2
client/windows/settings/AdventureOptionsTab.cpp

@@ -105,11 +105,15 @@ AdventureOptionsTab::AdventureOptionsTab()
 	addCallback("mapSwipeChanged", [](bool value)
 	{
 #if defined(VCMI_MOBILE)
-		setBoolSetting("general", "swipe", value);
+		return setBoolSetting("general", "swipe", value);
 #else
-		setBoolSetting("general", "swipeDesktop", value);
+		return setBoolSetting("general", "swipeDesktop", value);
 #endif
 	});
+	addCallback("infoBarPickChanged", [](bool value)
+	{
+		return setBoolSetting("gameTweaks", "infoBarPick", value);
+	});
 	build(config);
 
 	std::shared_ptr<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());
 }

+ 5 - 0
config/schemas/settings.json

@@ -503,6 +503,7 @@
 				"numericCreaturesQuantities",
 				"availableCreaturesAsDwellingLabel",
 				"compactTownCreatureInfo",
+				"infoBarPick",
 				"skipBattleIntroMusic"
 			],
 			"properties": {
@@ -526,6 +527,10 @@
 					"type" : "boolean",
 					"default" : false
 				},
+				"infoBarPick" : {
+					"type" : "boolean",
+					"default" : false
+				},
 				"skipBattleIntroMusic" : {
 					"type" : "boolean",
 					"default" : false

+ 12 - 1
config/widgets/settings/adventureOptionsTab.json

@@ -364,6 +364,10 @@
 				{
 					"position": {"x": 45, "y": 385},
 					"text": "vcmi.adventureOptions.mapSwipe.hover"
+				},
+				{
+					"position": {"x": 45, "y": 415},
+					"text": "vcmi.adventureOptions.infoBarPick.hover"
 				}
 			]
 		},
@@ -399,6 +403,13 @@
 			"position": {"x": 10, "y": 383},
 			"callback": "mapSwipeChanged"
 		},
-
+		{
+			"name": "infoBarPickCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.adventureOptions.infoBarPick",
+			"position": {"x": 10, "y": 413},
+			"callback": "infoBarPickChanged"
+		}
 	]
 }